small-fdw-changes.patch
text/x-diff
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: unified
| File | + | − |
|---|---|---|
| doc/src/sgml/ddl.sgml | 35 | 0 |
| doc/src/sgml/fdwhandler.sgml | 125 | 0 |
| doc/src/sgml/filelist.sgml | 2 | 1 |
| doc/src/sgml/postgres.sgml | 1 | 0 |
| src/backend/commands/foreigncmds.c | 10 | 5 |
| src/backend/executor/execAmi.c | 1 | 1 |
| src/backend/executor/execProcnode.c | 1 | 1 |
| src/backend/executor/nodeForeignscan.c | 8 | 6 |
| src/backend/foreign/foreign.c | 5 | 8 |
| src/backend/nodes/copyfuncs.c | 0 | 3 |
| src/backend/nodes/outfuncs.c | 2 | 2 |
| src/backend/optimizer/path/allpaths.c | 1 | 1 |
| src/backend/optimizer/plan/createplan.c | 3 | 3 |
| src/backend/optimizer/prep/prepunion.c | 4 | 3 |
| src/backend/optimizer/util/pathnode.c | 1 | 1 |
| src/backend/optimizer/util/plancat.c | 1 | 1 |
| src/bin/pg_dump/pg_dump.c | 24 | 9 |
| src/include/foreign/fdwapi.h | 11 | 14 |
| src/include/nodes/execnodes.h | 1 | 1 |
diff --git a/doc/src/sgml/ddl.sgml b/doc/src/sgml/ddl.sgml
index a65b4bc..06a82a4 100644
--- a/doc/src/sgml/ddl.sgml
+++ b/doc/src/sgml/ddl.sgml
@@ -2986,6 +2986,41 @@ ANALYZE measurement;
</sect2>
</sect1>
+ <sect1 id="ddl-foreign-data">
+ <title>Foreign Data</title>
+ <indexterm>
+ <primary>foreign data</primary>
+ </indexterm>
+ <para>
+ <productname>PostgreSQL</productname> implements parts of the SQL/MED
+ specification, which allows you to access external data that resides
+ outside PostgreSQL, using normal SQL queries.
+ </para>
+
+ <para>
+ Foreign data is accessed with help from a
+ <firstterm>foreign data wrapper</firstterm>. A foreign data wrapper is a
+ library that can communicate with an external data source, hiding the
+ details of connecting to the data source and fetching data from it. There
+ are several foreign data wrappers available for e.g reading files residing
+ on the server, or to connect to another PostgreSQL instance. If none of
+ the existing foreign data wrappers suite your needs, you can write your
+ own, see <xref linkend="fdwhandler">.
+ </para>
+
+ <para>
+ To access foreign data, you need to create a foreign server to define
+ the connection details to the external data source, using options required
+ by the foreign data wrapper. Then you need to create one or more
+ <firstterm>foreign tables</firstterm> that define the structure of the
+ remote data. A foreign table can be used in queries just like a normal
+ table, but a foreign table has no storage in the PostgreSQL server.
+ Whenever it is used, PostgreSQL asks the foreign data wrapper to fetch
+ the data from the external source.
+ </para>
+
+ </sect1>
+
<sect1 id="ddl-others">
<title>Other Database Objects</title>
diff --git a/doc/src/sgml/fdwhandler.sgml b/doc/src/sgml/fdwhandler.sgml
new file mode 100644
index 0000000..8ef93bc
--- /dev/null
+++ b/doc/src/sgml/fdwhandler.sgml
@@ -0,0 +1,125 @@
+<!-- doc/src/sgml/fdwhandler.sgml -->
+
+ <chapter id="fdwhandler">
+ <title>Writing A Foreign Data Wrapper</title>
+
+ <indexterm zone="fdwhandler">
+ <primary>foreign data wrapper</primary>
+ <secondary>handler for</secondary>
+ </indexterm>
+
+ <para>
+ All operations on foreign tables are handled through a foreign data
+ wrapper, which consists of a set of functions that the planner and
+ executor call. The foreign data wrapper is responsible for fetching
+ data from the remote data source and returning it to the PostgreSQL
+ executor. This chapter outlines how to write a new foreign data wrapper.
+ </para>
+
+ <para>
+ The FDW provider needs to implement a handler function, and (optionally)
+ a validator function. Both functions must be written in a compiled
+ language such as C, using the version-1 interface.
+
+ The validator function must be registered with
+ <productname>PostgreSQL</productname> as taking two arguments, a text
+ array containing the options, and an oid representing the type of object
+ the options are to be associated with. The handler function must be
+ registered as taking no arguments, and returning the type
+ <type>fdw_handler</type>. This special pseudotype identifies the
+ function as a fdw handler and prevents it from being called directly in
+ SQL commands. For more details on C language calling conventions and
+ dynamic loading, see <xref linkend="xfunc-c">.
+ </para>
+
+ <para>
+ The validator function is responsible for validating
+ options given in the CREATE FOREIGN DATA WRAPPER, CREATE SERVER and
+ CREATE FOREIGN TABLE commands.
+ </para>
+
+ <para>
+ The handler function returns a struct of function pointers to callback
+ functions that are called by the planner and executor.
+ </para>
+
+ <para>
+ The SQL standard specifies an interface for writing foreign data wrappers.
+ However, PostgreSQL does not implement that API, because the effort to
+ accommodate it into PostgreSQL would be large. The standard API hasn't
+ gained wide adoption anyway, there aren't many standard-compliant foreign
+ data wrappers out there.
+ </para>
+
+ <para>
+ The foreign data wrappers included in the standard distribution
+ are good references when trying to write your own.
+ Look into the <filename>contrib/file_fdw</> subdirectory of the source tree.
+ The <xref linkend="sql-createforeigndatawrapper">
+ reference page also has some useful details.
+ </para>
+
+ <sect1 id="fdw-routines">
+ <title>Foreign Data Wrapper routines</title>
+
+ <para>
+ The FDW handler function returns a FdwRoutine struct with the following
+ callback functions:
+ </para>
+
+ <para>
+<programlisting>
+FdwPlan *
+PlanRelScan (Oid foreigntableid,
+ PlannerInfo *root,
+ RelOptInfo *baserel);
+</programlisting>
+ Plan a scan on a foreign table. This is called when a query is planned.
+ The function must return a palloc'd struct containing cost estimates,
+ a string to show for this scan in EXPLAIN, and any FDW-private information
+ that is needed to execute the foreign scan at a later stage.
+ A prepared plan may be executed many times.
+ </para>
+
+ <para>
+<programlisting>
+FdwExecutionState *
+BeginScan (FdwPlan *plan
+ ParamListInfo *params);
+</programlisting>
+ Begin exeucuting a foreign scan. This is called when a query is executed.
+ The function must return a palloc'd struct containing any private
+ information needed by the Iterate and EndScan calls that follow.
+ </para>
+
+ <para>
+<programlisting>
+void
+Iterate (FdwExecutionState *state,
+ TupleTableSlot *slot);
+</programlisting>
+ Fetch one row from the foreign source. Note that this is called in a
+ short-lived memory context that may be reset between every invocation of
+ Iterate. Create a memory context in BeginScan if you need longer-lived
+ storage.
+ </para>
+
+ <para>
+<programlisting>
+void
+ReScan (FdwExecutionState *state);
+</programlisting>
+ Restarts the scan.
+ </para>
+
+ <para>
+<programlisting>
+void
+EndScan (FdwExecutionState *state);
+</programlisting>
+ Ends the scan.
+ </para>
+
+ </sect1>
+
+ </chapter>
diff --git a/doc/src/sgml/filelist.sgml b/doc/src/sgml/filelist.sgml
index 99437ac..c2f4169 100644
--- a/doc/src/sgml/filelist.sgml
+++ b/doc/src/sgml/filelist.sgml
@@ -82,10 +82,11 @@
<!entity geqo SYSTEM "geqo.sgml">
<!entity gist SYSTEM "gist.sgml">
<!entity gin SYSTEM "gin.sgml">
-<!entity planstats SYSTEM "planstats.sgml">
+<!entity planstats SYSTEM "planstats.sgml">
<!entity indexam SYSTEM "indexam.sgml">
<!entity nls SYSTEM "nls.sgml">
<!entity plhandler SYSTEM "plhandler.sgml">
+<!entity fdwhandler SYSTEM "fdwhandler.sgml">
<!entity protocol SYSTEM "protocol.sgml">
<!entity sources SYSTEM "sources.sgml">
<!entity storage SYSTEM "storage.sgml">
diff --git a/doc/src/sgml/postgres.sgml b/doc/src/sgml/postgres.sgml
index 4d32f7d..98d19a5 100644
--- a/doc/src/sgml/postgres.sgml
+++ b/doc/src/sgml/postgres.sgml
@@ -238,6 +238,7 @@
&sources;
&nls;
&plhandler;
+ &fdwhandler;
&geqo;
&indexam;
&gist;
diff --git a/src/backend/commands/foreigncmds.c b/src/backend/commands/foreigncmds.c
index 360016c..6acdc9c 100644
--- a/src/backend/commands/foreigncmds.c
+++ b/src/backend/commands/foreigncmds.c
@@ -330,6 +330,9 @@ lookup_fdw_validator_func(DefElem *validator)
/* return value is ignored, so we don't check the type */
}
+/*
+ * Convert a handler function name passed from the parser to an Oid
+ */
static Oid
lookup_fdw_handler_func(DefElem *handler)
{
@@ -365,19 +368,21 @@ parse_func_options(List *func_options, DefElem **validator, DefElem **handler)
if (pg_strcasecmp(def->defname, "validator") == 0)
{
if (*validator)
- elog(ERROR, "duplicated VALIDATOR");
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("conflicting or redundant options")));
*validator = def;
}
else if (pg_strcasecmp(def->defname, "handler") == 0)
{
if (*handler)
- elog(ERROR, "duplicated HANDLER");
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("conflicting or redundant options")));
*handler = def;
}
else
- {
- elog(ERROR, "invalid option");
- }
+ elog(ERROR, "option \"%s\" not recognized", def->defname);
}
}
diff --git a/src/backend/executor/execAmi.c b/src/backend/executor/execAmi.c
index b27de28..9f7b138 100644
--- a/src/backend/executor/execAmi.c
+++ b/src/backend/executor/execAmi.c
@@ -21,8 +21,8 @@
#include "executor/nodeBitmapIndexscan.h"
#include "executor/nodeBitmapOr.h"
#include "executor/nodeCtescan.h"
-#include "executor/nodeFunctionscan.h"
#include "executor/nodeForeignscan.h"
+#include "executor/nodeFunctionscan.h"
#include "executor/nodeGroup.h"
#include "executor/nodeGroup.h"
#include "executor/nodeHash.h"
diff --git a/src/backend/executor/execProcnode.c b/src/backend/executor/execProcnode.c
index 92b83ac..f275b41 100644
--- a/src/backend/executor/execProcnode.c
+++ b/src/backend/executor/execProcnode.c
@@ -85,8 +85,8 @@
#include "executor/nodeBitmapIndexscan.h"
#include "executor/nodeBitmapOr.h"
#include "executor/nodeCtescan.h"
-#include "executor/nodeFunctionscan.h"
#include "executor/nodeForeignscan.h"
+#include "executor/nodeFunctionscan.h"
#include "executor/nodeGroup.h"
#include "executor/nodeHash.h"
#include "executor/nodeHashjoin.h"
diff --git a/src/backend/executor/nodeForeignscan.c b/src/backend/executor/nodeForeignscan.c
index e6354c7..3183f43 100644
--- a/src/backend/executor/nodeForeignscan.c
+++ b/src/backend/executor/nodeForeignscan.c
@@ -1,7 +1,7 @@
/*-------------------------------------------------------------------------
*
* nodeForeignscan.c
- * Support routines for sequential scans of foreign tables.
+ * Support routines for scans of foreign tables.
*
* Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
@@ -14,9 +14,9 @@
*/
/*
* INTERFACE ROUTINES
- * ExecForeignScan sequentially scans a foreign table.
- * ExecForeignNext retrieve next tuple in sequential order.
- * ExecInitForeignScan creates and initializes a seqscan node.
+ * ExecForeignScan scans a foreign table.
+ * ExecForeignNext retrieve next tuple.
+ * ExecInitForeignScan creates and initializes a foreign scan node.
* ExecEndForeignScan releases any storage allocated.
* ExecForeignReScan rescans the foreign table
* ExecForeignMarkPos marks scan position
@@ -76,6 +76,8 @@ ForeignNext(ForeignScanState *node)
ExecMaterializeSlot(slot);
/* overwrite only tableoid of the tuple */
+ /* XXX: Isn't this a bit dirty? We're scribbling over the field in
+ * the heap tuple that might've been allocated by the FDW. */
slot->tts_tuple->t_tableOid =
RelationGetRelid(node->ss.ss_currentRelation);
}
@@ -180,8 +182,8 @@ ExecInitForeignScan(ForeignScan *node, EState *estate, int eflags)
scanstate->routine = routine;
/*
- * If this execution was not for EXPLAIN w/o ANALYZE flag, initiate the
- * foreign scan.
+ * Tell the FDW to initiate the foreign scan, unless we're just doing
+ * EXPLAIN (ie, aren't going to run the plan).
*/
if (!(eflags & EXEC_FLAG_EXPLAIN_ONLY))
{
diff --git a/src/backend/foreign/foreign.c b/src/backend/foreign/foreign.c
index 1fdd925..3da8541 100644
--- a/src/backend/foreign/foreign.c
+++ b/src/backend/foreign/foreign.c
@@ -263,10 +263,7 @@ GetForeignTable(Oid relid)
Datum datum;
bool isnull;
- tp = SearchSysCache(FOREIGNTABLEREL,
- ObjectIdGetDatum(relid),
- 0, 0, 0);
-
+ tp = SearchSysCache1(FOREIGNTABLEREL, ObjectIdGetDatum(relid));
if (!HeapTupleIsValid(tp))
elog(ERROR, "cache lookup failed for foreign table %u", relid);
@@ -297,10 +294,10 @@ GetForeignTable(Oid relid)
FdwRoutine *
GetFdwRoutine(Oid fdwhandler)
{
- FmgrInfo flinfo;
- FunctionCallInfoData fcinfo;
- Datum result;
- FdwRoutine *routine;
+ FmgrInfo flinfo;
+ FunctionCallInfoData fcinfo;
+ Datum result;
+ FdwRoutine *routine;
if (fdwhandler == InvalidOid)
elog(ERROR, "foreign-data wrapper has no handler");
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 79c71c4..61c8599 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -572,9 +572,6 @@ _copyFdwPlan(FdwPlan *from)
{
FdwPlan *newnode = makeNode(FdwPlan);
- /*
- * copy node superclass fields
- */
COPY_STRING_FIELD(explainInfo);
COPY_SCALAR_FIELD(startup_cost);
COPY_SCALAR_FIELD(total_cost);
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index dee46ba..4602ced 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -546,8 +546,8 @@ _outFdwPlan(StringInfo str, FdwPlan *node)
WRITE_NODE_TYPE("FDWPLAN");
WRITE_STRING_FIELD(explainInfo);
- WRITE_FLOAT_FIELD(startup_cost, "%.2f");
- WRITE_FLOAT_FIELD(total_cost, "%.2f");
+ WRITE_FLOAT_FIELD(startup_cost, "%.2f");
+ WRITE_FLOAT_FIELD(total_cost, "%.2f");
WRITE_NODE_FIELD(private);
}
diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c
index ae2e134..c196ab6 100644
--- a/src/backend/optimizer/path/allpaths.c
+++ b/src/backend/optimizer/path/allpaths.c
@@ -258,7 +258,7 @@ set_plain_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
if (IsForeignTable(rte->relid))
{
- /* only foreign scan path is applyable to foreign table */
+ /* only foreign scan path is applicable to a foreign table */
add_path(rel, create_foreignscan_path(root, rel));
}
else
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index 938b8af..53ae495 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -352,9 +352,9 @@ create_scan_plan(PlannerInfo *root, Path *best_path)
case T_ForeignScan:
plan = (Plan *) create_foreignscan_plan(root,
- best_path,
- tlist,
- scan_clauses);
+ best_path,
+ tlist,
+ scan_clauses);
break;
default:
diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c
index 61f6d5f..2605efb 100644
--- a/src/backend/optimizer/prep/prepunion.c
+++ b/src/backend/optimizer/prep/prepunion.c
@@ -1238,13 +1238,14 @@ expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti)
}
/*
- * SELECT FOR UPDATE/SHARE is not allowd to foreign tables because
+ * SELECT FOR UPDATE/SHARE is not allowed on foreign tables because
* they are read-only.
*/
if (newrelation->rd_rel->relkind == RELKIND_FOREIGN_TABLE &&
lockmode != AccessShareLock)
- ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("SELECT FOR UPDATE/SHARE is not allowed with foreign tables")));
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("SELECT FOR UPDATE/SHARE is not allowed with foreign tables")));
/*
* Build an RTE for the child, and attach to query's rangetable list.
diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c
index 134ec7e..d53f7aa 100644
--- a/src/backend/optimizer/util/pathnode.c
+++ b/src/backend/optimizer/util/pathnode.c
@@ -1435,7 +1435,7 @@ create_foreignscan_path(PlannerInfo *root, RelOptInfo *rel)
pathnode->path.pathtype = T_ForeignScan;
pathnode->path.parent = rel;
pathnode->path.pathkeys = NIL; /* result is always unordered */
-
+
rte = planner_rt_fetch(rel->relid, root);
routine = GetFdwRoutineByRelId(rte->relid);
if (routine->PlanRelScan == NULL)
diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c
index 1d60af8..8d477a6 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -455,7 +455,7 @@ estimate_rel_size(Relation rel, int32 *attr_widths,
*tuples = 1;
break;
case RELKIND_FOREIGN_TABLE:
- /* foreign tables has no storage, trust statistics */
+ /* foreign tables have no storage, trust statistics */
*pages = rel->rd_rel->relpages;
*tuples = rel->rd_rel->reltuples;
break;
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 2a381a0..23cc95a 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -5986,14 +5986,30 @@ getForeignDataWrappers(int *numForeignDataWrappers)
/* Make sure we are in proper schema */
selectSourceSchema("pg_catalog");
- appendPQExpBuffer(query, "SELECT tableoid, oid, fdwname, "
- "(%s fdwowner) AS rolname, fdwvalidator::pg_catalog.regproc, "
- "fdwhandler::pg_catalog.regproc, fdwacl,"
- "array_to_string(ARRAY("
- " SELECT option_name || ' ' || quote_literal(option_value) "
- " FROM pg_options_to_table(fdwoptions)), ', ') AS fdwoptions "
- "FROM pg_foreign_data_wrapper",
- username_subquery);
+ if (g_fout->remoteVersion >= 90100)
+ {
+ appendPQExpBuffer(query, "SELECT tableoid, oid, fdwname, "
+ "(%s fdwowner) AS rolname, "
+ "fdwvalidator::pg_catalog.regproc, "
+ "fdwhandler::pg_catalog.regproc, fdwacl,"
+ "array_to_string(ARRAY("
+ " SELECT option_name || ' ' || quote_literal(option_value) "
+ " FROM pg_options_to_table(fdwoptions)), ', ') AS fdwoptions "
+ "FROM pg_foreign_data_wrapper",
+ username_subquery);
+ }
+ else
+ {
+ appendPQExpBuffer(query, "SELECT tableoid, oid, fdwname, "
+ "(%s fdwowner) AS rolname, "
+ "fdwvalidator::pg_catalog.regproc, "
+ "0::oid AS fdwhandler, fdwacl,"
+ "array_to_string(ARRAY("
+ " SELECT option_name || ' ' || quote_literal(option_value) "
+ " FROM pg_options_to_table(fdwoptions)), ', ') AS fdwoptions "
+ "FROM pg_foreign_data_wrapper",
+ username_subquery);
+ }
res = PQexec(g_conn, query->data);
check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
@@ -6026,7 +6042,6 @@ getForeignDataWrappers(int *numForeignDataWrappers)
fdwinfo[i].fdwoptions = strdup(PQgetvalue(res, i, i_fdwoptions));
fdwinfo[i].fdwacl = strdup(PQgetvalue(res, i, i_fdwacl));
-
/* Decide whether we want to dump it */
selectDumpableObject(&(fdwinfo[i].dobj));
}
diff --git a/src/include/foreign/fdwapi.h b/src/include/foreign/fdwapi.h
index 7be1999..9afdbe7 100644
--- a/src/include/foreign/fdwapi.h
+++ b/src/include/foreign/fdwapi.h
@@ -21,7 +21,7 @@
* context with copyObject. It means that FdwPlan, a part of ForeignScan plan
* node, and its contents must have copyObject support too.
*/
-struct FdwPlan
+typedef struct
{
NodeTag type;
@@ -46,20 +46,18 @@ struct FdwPlan
#endif
/*
- * FDW-private data. FDW must guarantee that every elements in this list
- * have copyObject support. If FDW needs to store arbitrary data such as
- * non-Node structure, Const of bytea would be able to use as a container.
+ * FDW-private data. Anything stored here needs to have copyObject
+ * support. If FDW needs to store arbitrary data such as a non-Node
+ * structure, Const of bytea can be used as a container.
*/
- List *private;
-};
-typedef struct FdwPlan FdwPlan;
+ Node *private;
+} FdwPlan;
-struct FdwExecutionState
+typedef struct
{
- /* FDW-private data */
+ /* FDW-private data. This doesn't need to have copyObject support. */
void *private;
-};
-typedef struct FdwExecutionState FdwExecutionState;
+} FdwExecutionState;
/*
* Common interface routines of FDW, inspired by the FDW API in the SQL/MED
@@ -72,7 +70,7 @@ typedef struct FdwExecutionState FdwExecutionState;
* with BeginScan. The implementation should fill in the cost estimates in
* FdwPlan, and may store private information.
*/
-struct FdwRoutine
+typedef struct
{
/*
* Plan a scan on a foreign table. 'foreigntableid' identifies the foreign
@@ -144,7 +142,6 @@ struct FdwRoutine
*/
FdwPlan *(*PlanQuery)(PlannerInfo *root, Query query);
#endif
-};
-typedef struct FdwRoutine FdwRoutine;
+} FdwRoutine;
#endif /* FDWAPI_H */
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index 7d1c681..561d512 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -1415,7 +1415,7 @@ typedef struct ForeignScanState
{
ScanState ss; /* its first field is NodeTag */
FdwRoutine *routine;
- FdwExecutionState *fstate; /* private data for each data wrapper */
+ FdwExecutionState *fstate; /* private state of the wrapper */
} ForeignScanState;
/* ----------------------------------------------------------------