]> granicus.if.org Git - postgresql/commitdiff
Implement an API to let foreign-data wrappers actually be functional.
authorTom Lane <tgl@sss.pgh.pa.us>
Sun, 20 Feb 2011 05:17:18 +0000 (00:17 -0500)
committerTom Lane <tgl@sss.pgh.pa.us>
Sun, 20 Feb 2011 05:18:14 +0000 (00:18 -0500)
This commit provides the core code and documentation needed.  A contrib
module test case will follow shortly.

Shigeru Hanada, Jan Urbanski, Heikki Linnakangas

39 files changed:
doc/src/sgml/ddl.sgml
doc/src/sgml/fdwhandler.sgml [new file with mode: 0644]
doc/src/sgml/filelist.sgml
doc/src/sgml/postgres.sgml
doc/src/sgml/ref/create_foreign_data_wrapper.sgml
doc/src/sgml/ref/create_foreign_table.sgml
src/backend/commands/explain.c
src/backend/executor/Makefile
src/backend/executor/execAmi.c
src/backend/executor/execMain.c
src/backend/executor/execProcnode.c
src/backend/executor/nodeForeignscan.c [new file with mode: 0644]
src/backend/foreign/foreign.c
src/backend/nodes/copyfuncs.c
src/backend/nodes/outfuncs.c
src/backend/optimizer/README
src/backend/optimizer/path/allpaths.c
src/backend/optimizer/path/costsize.c
src/backend/optimizer/plan/createplan.c
src/backend/optimizer/plan/planner.c
src/backend/optimizer/plan/setrefs.c
src/backend/optimizer/plan/subselect.c
src/backend/optimizer/util/pathnode.c
src/backend/optimizer/util/plancat.c
src/backend/parser/analyze.c
src/backend/rewrite/rewriteHandler.c
src/backend/utils/fmgr/fmgr.c
src/include/commands/explain.h
src/include/executor/nodeForeignscan.h [new file with mode: 0644]
src/include/fmgr.h
src/include/foreign/fdwapi.h [new file with mode: 0644]
src/include/foreign/foreign.h
src/include/nodes/execnodes.h
src/include/nodes/nodes.h
src/include/nodes/plannodes.h
src/include/nodes/relation.h
src/include/optimizer/cost.h
src/include/optimizer/pathnode.h
src/test/regress/expected/foreign_data.out

index a65b4bcd338c8516d751132c5ccbc85c6873befa..12f7c3706e84b0f614195a7d8e5dee52f74baef6 100644 (file)
@@ -2986,6 +2986,53 @@ ANALYZE measurement;
   </sect2>
  </sect1>
 
+ <sect1 id="ddl-foreign-data">
+  <title>Foreign Data</title>
+
+   <indexterm>
+    <primary>foreign data</primary>
+   </indexterm>
+   <indexterm>
+    <primary>foreign table</primary>
+   </indexterm>
+
+   <para>
+    <productname>PostgreSQL</productname> implements portions of the SQL/MED
+    specification, allowing you to access data that resides outside
+    PostgreSQL using regular SQL queries.  Such data is referred to as
+    <firstterm>foreign data</>.  (Note that this usage is not to be confused
+    with foreign keys, which are a type of constraint within the database.)
+   </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, which can for example read
+    plain data files residing on the server, or connect to another PostgreSQL
+    instance. If none of the existing foreign data wrappers suit your needs,
+    you can write your own; see <xref linkend="fdwhandler">.
+   </para>
+
+   <para>
+    To access foreign data, you need to create a <firstterm>foreign server</>
+    object, which defines how to connect to a particular external data source,
+    according to the set of options used by a particular foreign data
+    wrapper. Then you need to create one or more <firstterm>foreign
+    tables</firstterm>, which 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>
+
+   <para>
+    Currently, foreign tables are read-only.  This limitation may be fixed
+    in a future release.
+   </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 (file)
index 0000000..fc07f12
--- /dev/null
@@ -0,0 +1,212 @@
+<!-- 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 a foreign table are handled through its 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
+    <productname>PostgreSQL</productname> executor. This chapter outlines how
+    to write a new foreign data wrapper.
+   </para>
+
+   <para>
+    The FDW author 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.
+    For details on C language calling conventions and dynamic loading,
+    see <xref linkend="xfunc-c">.
+   </para>
+
+   <para>
+    The handler function simply returns a struct of function pointers to
+    callback functions that will be called by the planner and executor.
+    Most of the effort in writing an FDW is in implementing these callback
+    functions.
+    The handler function must be registered with
+    <productname>PostgreSQL</productname> as taking no arguments and returning
+    the special pseudo-type <type>fdw_handler</type>.
+    The callback functions are plain C functions and are not visible or
+    callable at the SQL level.
+   </para>
+
+   <para>
+    The validator function is responsible for validating options given in the
+    <command>CREATE FOREIGN DATA WRAPPER</command>, <command>CREATE
+    SERVER</command> and <command>CREATE FOREIGN TABLE</command> commands.
+    The validator function must be registered as taking two arguments, a text
+    array containing the options to be validated, and an OID representing the
+    type of object the options are associated with (in the form of the OID
+    of the system catalog the object would be stored in).  If no validator
+    function is supplied, the options are not checked at object creation time.
+   </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>
+
+   <note>
+    <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, and the standard API hasn't
+     gained wide adoption anyway.
+    </para>
+   </note>
+
+   <sect1 id="fdw-routines">
+    <title>Foreign Data Wrapper Callback Routines</title>
+
+    <para>
+     The FDW handler function returns a palloc'd <structname>FdwRoutine</>
+     struct containing pointers to the following callback functions:
+    </para>
+
+    <para>
+<programlisting>
+FdwPlan *
+PlanForeignScan (Oid foreigntableid,
+                 PlannerInfo *root,
+                 RelOptInfo *baserel);
+</programlisting>
+
+     Plan a scan on a foreign table. This is called when a query is planned.
+     <literal>foreigntableid</> is the <structname>pg_class</> OID of the
+     foreign table.  <literal>root</> is the planner's global information
+     about the query, and <literal>baserel</> is the planner's information
+     about this table.
+     The function must return a palloc'd struct that contains cost estimates
+     plus any FDW-private information that is needed to execute the foreign
+     scan at a later time.  (Note that the private information must be
+     represented in a form that <function>copyObject</> knows how to copy.)
+    </para>
+
+    <para>
+     The information in <literal>root</> and <literal>baserel</> can be used
+     to reduce the amount of information that has to be fetched from the
+     foreign table (and therefore reduce the cost estimate).
+     <literal>baserel-&gt;baserestrictinfo</> is particularly interesting, as
+     it contains restriction quals (<literal>WHERE</> clauses) that can be
+     used to filter the rows to be fetched.  (The FDW is not required to
+     enforce these quals, as the finished plan will recheck them anyway.)
+     <literal>baserel-&gt;reltargetlist</> can be used to determine which
+     columns need to be fetched.
+    </para>
+
+    <para>
+     In addition to returning cost estimates, the function should update
+     <literal>baserel-&gt;rows</> to be the expected number of rows returned
+     by the scan, after accounting for the filtering done by the restriction
+     quals.  The initial value of <literal>baserel-&gt;rows</> is just a
+     constant default estimate, which should be replaced if at all possible.
+     The function may also choose to update <literal>baserel-&gt;width</> if
+     it can compute a better estimate of the average result row width.
+    </para>
+
+    <para>
+<programlisting>
+void
+ExplainForeignScan (ForeignScanState *node,
+                    ExplainState *es);
+</programlisting>
+
+     Print additional <command>EXPLAIN</> output for a foreign table scan.
+     This can just return if there is no need to print anything.
+     Otherwise, it should call <function>ExplainPropertyText</> and
+     related functions to add fields to the <command>EXPLAIN</> output.
+     The flag fields in <literal>es</> can be used to determine what to
+     print, and the state of the <structname>ForeignScanState</> node
+     can be inspected to provide runtime statistics in the <command>EXPLAIN
+     ANALYZE</> case.
+    </para>
+
+    <para>
+<programlisting>
+void
+BeginForeignScan (ForeignScanState *node,
+                  int eflags);
+</programlisting>
+
+     Begin executing a foreign scan. This is called during executor startup.
+     It should perform any initialization needed before the scan can start.
+     The <structname>ForeignScanState</> node has already been created, but
+     its <structfield>fdw_state</> field is still NULL.  Information about
+     the table to scan is accessible through the
+     <structname>ForeignScanState</> node (in particular, from the underlying
+     <structname>ForeignScan</> plan node, which contains a pointer to the
+     <structname>FdwPlan</> structure returned by
+     <function>PlanForeignScan</>).
+    </para>
+
+    <para>
+     Note that when <literal>(eflags &amp; EXEC_FLAG_EXPLAIN_ONLY)</> is
+     true, this function should not perform any externally-visible actions;
+     it should only do the minimum required to make the node state valid
+     for <function>ExplainForeignScan</> and <function>EndForeignScan</>.
+    </para>
+
+    <para>
+<programlisting>
+TupleTableSlot *
+IterateForeignScan (ForeignScanState *node);
+</programlisting>
+
+     Fetch one row from the foreign source, returning it in a tuple table slot
+     (the node's <structfield>ScanTupleSlot</> should be used for this
+     purpose).  Return NULL if no more rows are available.  The tuple table
+     slot infrastructure allows either a physical or virtual tuple to be
+     returned; in most cases the latter choice is preferable from a
+     performance standpoint.  Note that this is called in a short-lived memory
+     context that will be reset between invocations.  Create a memory context
+     in <function>BeginForeignScan</> if you need longer-lived storage, or use
+     the <structfield>es_query_cxt</> of the node's <structname>EState</>.
+    </para>
+
+    <para>
+     The rows returned must match the column signature of the foreign table
+     being scanned.  If you choose to optimize away fetching columns that
+     are not needed, you should insert nulls in those column positions.
+    </para>
+
+    <para>
+<programlisting>
+void
+ReScanForeignScan (ForeignScanState *node);
+</programlisting>
+
+     Restart the scan from the beginning.  Note that any parameters the
+     scan depends on may have changed value, so the new scan does not
+     necessarily return exactly the same rows.
+    </para>
+
+    <para>
+<programlisting>
+void
+EndForeignScan (ForeignScanState *node);
+</programlisting>
+
+     End the scan and release resources.  It is normally not important
+     to release palloc'd memory, but for example open files and connections
+     to remote servers should be cleaned up.
+    </para>
+
+    <para>
+     The <structname>FdwRoutine</> and <structname>FdwPlan</> struct types
+     are declared in <filename>src/include/foreign/fdwapi.h</>, which see
+     for additional details.
+    </para>
+
+   </sect1>
+
+ </chapter>
index b9d4ea59b1aaa2f5eef02289ab9b937ad9ee1213..659bcba7c78871c0ca39d8835f1c0129da2b1fc1 100644 (file)
@@ -86,6 +86,7 @@
 <!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">
index 4d32f7db259fba3fbce1197a31547e8ab35143b8..98d19a5c733dc039ca14a6ceafe89dfa94d83c37 100644 (file)
   &sources;
   &nls;
   &plhandler;
+  &fdwhandler;
   &geqo;
   &indexam;
   &gist;
index 711f32b118b31d5c4b04c100d0d23c6939f1bdb9..3093ebcb4ac57f95cc8cbe96dec7137bd2445082 100644 (file)
@@ -119,18 +119,13 @@ CREATE FOREIGN DATA WRAPPER <replaceable class="parameter">name</replaceable>
   <title>Notes</title>
 
   <para>
-   At the moment, the foreign-data wrapper functionality is very
-   rudimentary.  The purpose of foreign-data wrappers, foreign
-   servers, and user mappings is to store this information in a
-   standard way so that it can be queried by interested applications.
-   One such application is <application>dblink</application>;
-   see <xref linkend="dblink">.  The functionality to actually query
-   external data through a foreign-data wrapper library does not exist
-   yet.
+   At the moment, the foreign-data wrapper functionality is rudimentary.
+   There is no support for updating a foreign table, and optimization of
+   queries is primitive (and mostly left to the wrapper, too).
   </para>
 
   <para>
-   There is currently one foreign-data wrapper validator function
+   There is one built-in foreign-data wrapper validator function
    provided:
    <filename>postgresql_fdw_validator</filename>, which accepts
    options corresponding to <application>libpq</> connection
index ac2e1393e38ba86c6b7edb3c1c5d986b34d8916a..77c62140f281bb4c6dc9f61170a32faf71b4f2ed 100644 (file)
@@ -131,8 +131,8 @@ CREATE FOREIGN TABLE [ IF NOT EXISTS ] <replaceable class="PARAMETER">table_name
      <para>
       Options to be associated with the new foreign table.
       The allowed option names and values are specific to each foreign
-      data wrapper and are validated using the foreign-data wrapper
-      library. Option names must be unique. 
+      data wrapper and are validated using the foreign-data wrapper's
+      validator function. Option names must be unique.
      </para>
     </listitem>
    </varlistentry>
index 65345907d0a8afb8d44d0d5b544dfd63f8dc6f44..ccae1f9d84c9983785186e60fb6e0ee8f048fb62 100644 (file)
@@ -22,6 +22,7 @@
 #include "commands/trigger.h"
 #include "executor/hashjoin.h"
 #include "executor/instrument.h"
+#include "foreign/fdwapi.h"
 #include "optimizer/clauses.h"
 #include "optimizer/planner.h"
 #include "optimizer/var.h"
@@ -80,25 +81,15 @@ static void show_sort_keys_common(PlanState *planstate,
                                                                  List *ancestors, ExplainState *es);
 static void show_sort_info(SortState *sortstate, ExplainState *es);
 static void show_hash_info(HashState *hashstate, ExplainState *es);
+static void show_foreignscan_info(ForeignScanState *fsstate, ExplainState *es);
 static const char *explain_get_index_name(Oid indexId);
 static void ExplainScanTarget(Scan *plan, ExplainState *es);
 static void ExplainMemberNodes(List *plans, PlanState **planstates,
                                   List *ancestors, ExplainState *es);
 static void ExplainSubPlans(List *plans, List *ancestors,
                                                        const char *relationship, ExplainState *es);
-static void ExplainPropertyList(const char *qlabel, List *data,
-                                       ExplainState *es);
 static void ExplainProperty(const char *qlabel, const char *value,
                                bool numeric, ExplainState *es);
-
-#define ExplainPropertyText(qlabel, value, es) \
-       ExplainProperty(qlabel, value, false, es)
-static void ExplainPropertyInteger(const char *qlabel, int value,
-                                          ExplainState *es);
-static void ExplainPropertyLong(const char *qlabel, long value,
-                                       ExplainState *es);
-static void ExplainPropertyFloat(const char *qlabel, double value, int ndigits,
-                                        ExplainState *es);
 static void ExplainOpenGroup(const char *objtype, const char *labelname,
                                 bool labeled, ExplainState *es);
 static void ExplainCloseGroup(const char *objtype, const char *labelname,
@@ -705,6 +696,9 @@ ExplainNode(PlanState *planstate, List *ancestors,
                case T_WorkTableScan:
                        pname = sname = "WorkTable Scan";
                        break;
+               case T_ForeignScan:
+                       pname = sname = "Foreign Scan";
+                       break;
                case T_Material:
                        pname = sname = "Materialize";
                        break;
@@ -854,6 +848,7 @@ ExplainNode(PlanState *planstate, List *ancestors,
                case T_ValuesScan:
                case T_CteScan:
                case T_WorkTableScan:
+               case T_ForeignScan:
                        ExplainScanTarget((Scan *) plan, es);
                        break;
                case T_BitmapIndexScan:
@@ -1057,6 +1052,10 @@ ExplainNode(PlanState *planstate, List *ancestors,
                                show_scan_qual(plan->qual, "Filter", planstate, ancestors, es);
                        }
                        break;
+               case T_ForeignScan:
+                       show_scan_qual(plan->qual, "Filter", planstate, ancestors, es);
+                       show_foreignscan_info((ForeignScanState *) planstate, es);
+                       break;
                case T_NestLoop:
                        show_upper_qual(((NestLoop *) plan)->join.joinqual,
                                                        "Join Filter", planstate, ancestors, es);
@@ -1523,6 +1522,18 @@ show_hash_info(HashState *hashstate, ExplainState *es)
        }
 }
 
+/*
+ * Show extra information for a ForeignScan node.
+ */
+static void
+show_foreignscan_info(ForeignScanState *fsstate, ExplainState *es)
+{
+       FdwRoutine *fdwroutine = fsstate->fdwroutine;
+
+       /* Let the FDW emit whatever fields it wants */
+       fdwroutine->ExplainForeignScan(fsstate, es);
+}
+
 /*
  * Fetch the name of an index in an EXPLAIN
  *
@@ -1570,6 +1581,7 @@ ExplainScanTarget(Scan *plan, ExplainState *es)
                case T_IndexScan:
                case T_BitmapHeapScan:
                case T_TidScan:
+               case T_ForeignScan:
                        /* Assert it's on a real relation */
                        Assert(rte->rtekind == RTE_RELATION);
                        objectname = get_rel_name(rte->relid);
@@ -1695,7 +1707,7 @@ ExplainSubPlans(List *plans, List *ancestors,
  * Explain a property, such as sort keys or targets, that takes the form of
  * a list of unlabeled items.  "data" is a list of C strings.
  */
-static void
+void
 ExplainPropertyList(const char *qlabel, List *data, ExplainState *es)
 {
        ListCell   *lc;
@@ -1817,10 +1829,19 @@ ExplainProperty(const char *qlabel, const char *value, bool numeric,
        }
 }
 
+/*
+ * Explain a string-valued property.
+ */
+void
+ExplainPropertyText(const char *qlabel, const char *value, ExplainState *es)
+{
+       ExplainProperty(qlabel, value, false, es);
+}
+
 /*
  * Explain an integer-valued property.
  */
-static void
+void
 ExplainPropertyInteger(const char *qlabel, int value, ExplainState *es)
 {
        char            buf[32];
@@ -1832,7 +1853,7 @@ ExplainPropertyInteger(const char *qlabel, int value, ExplainState *es)
 /*
  * Explain a long-integer-valued property.
  */
-static void
+void
 ExplainPropertyLong(const char *qlabel, long value, ExplainState *es)
 {
        char            buf[32];
@@ -1845,7 +1866,7 @@ ExplainPropertyLong(const char *qlabel, long value, ExplainState *es)
  * Explain a float-valued property, using the specified number of
  * fractional digits.
  */
-static void
+void
 ExplainPropertyFloat(const char *qlabel, double value, int ndigits,
                                         ExplainState *es)
 {
index da4fbb440bc5cd9c41323d021945a9c4ac7069c5..a854c9a5dc68051cdab6a3832ad2b4b07b5a10d4 100644 (file)
@@ -23,6 +23,6 @@ OBJS = execAmi.o execCurrent.o execGrouping.o execJunk.o execMain.o \
        nodeSeqscan.o nodeSetOp.o nodeSort.o nodeUnique.o \
        nodeValuesscan.o nodeCtescan.o nodeWorktablescan.o \
        nodeGroup.o nodeSubplan.o nodeSubqueryscan.o nodeTidscan.o \
-       nodeWindowAgg.o tstoreReceiver.o spi.o
+       nodeForeignscan.o nodeWindowAgg.o tstoreReceiver.o spi.o
 
 include $(top_srcdir)/src/backend/common.mk
index 6bdc60c352d466e61b39ce3d2d53f18485b0dbc6..01775ce4494c1781bb630dcaeefa722af49b6a1a 100644 (file)
@@ -21,6 +21,7 @@
 #include "executor/nodeBitmapIndexscan.h"
 #include "executor/nodeBitmapOr.h"
 #include "executor/nodeCtescan.h"
+#include "executor/nodeForeignscan.h"
 #include "executor/nodeFunctionscan.h"
 #include "executor/nodeGroup.h"
 #include "executor/nodeGroup.h"
@@ -186,6 +187,10 @@ ExecReScan(PlanState *node)
                        ExecReScanWorkTableScan((WorkTableScanState *) node);
                        break;
 
+               case T_ForeignScanState:
+                       ExecReScanForeignScan((ForeignScanState *) node);
+                       break;
+
                case T_NestLoopState:
                        ExecReScanNestLoop((NestLoopState *) node);
                        break;
index 7df7f5cdf0d3a23ae3439445d695bcad7421ff57..68509fbfc6ec2576afd4e3f4a411fd39c4a89a96 100644 (file)
@@ -738,6 +738,13 @@ InitPlan(QueryDesc *queryDesc, int eflags)
                                break;
                }
 
+               /* if foreign table, tuples can't be locked */
+               if (relation && relation->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                                        errmsg("SELECT FOR UPDATE/SHARE cannot be used with foreign table \"%s\"",
+                                                       RelationGetRelationName(relation))));
+
                erm = (ExecRowMark *) palloc(sizeof(ExecRowMark));
                erm->relation = relation;
                erm->rti = rc->rti;
index e8ebec12343c98a7d00e8d14d527a05ed3120402..17788761d7f101319388f8ad7a6cb964efe140f3 100644 (file)
@@ -85,6 +85,7 @@
 #include "executor/nodeBitmapIndexscan.h"
 #include "executor/nodeBitmapOr.h"
 #include "executor/nodeCtescan.h"
+#include "executor/nodeForeignscan.h"
 #include "executor/nodeFunctionscan.h"
 #include "executor/nodeGroup.h"
 #include "executor/nodeHash.h"
@@ -232,6 +233,11 @@ ExecInitNode(Plan *node, EState *estate, int eflags)
                                                                                                                 estate, eflags);
                        break;
 
+               case T_ForeignScan:
+                       result = (PlanState *) ExecInitForeignScan((ForeignScan *) node,
+                                                                                                          estate, eflags);
+                       break;
+
                        /*
                         * join nodes
                         */
@@ -422,6 +428,10 @@ ExecProcNode(PlanState *node)
                        result = ExecWorkTableScan((WorkTableScanState *) node);
                        break;
 
+               case T_ForeignScanState:
+                       result = ExecForeignScan((ForeignScanState *) node);
+                       break;
+
                        /*
                         * join nodes
                         */
@@ -650,6 +660,10 @@ ExecEndNode(PlanState *node)
                        ExecEndWorkTableScan((WorkTableScanState *) node);
                        break;
 
+               case T_ForeignScanState:
+                       ExecEndForeignScan((ForeignScanState *) node);
+                       break;
+
                        /*
                         * join nodes
                         */
diff --git a/src/backend/executor/nodeForeignscan.c b/src/backend/executor/nodeForeignscan.c
new file mode 100644 (file)
index 0000000..c4309a9
--- /dev/null
@@ -0,0 +1,209 @@
+/*-------------------------------------------------------------------------
+ *
+ * nodeForeignscan.c
+ *       Routines to support scans of foreign tables
+ *
+ * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *       src/backend/executor/nodeForeignscan.c
+ *
+ *-------------------------------------------------------------------------
+ */
+/*
+ * INTERFACE ROUTINES
+ *
+ *             ExecForeignScan                 scans a foreign table.
+ *             ExecInitForeignScan             creates and initializes state info.
+ *             ExecReScanForeignScan   rescans the foreign relation.
+ *             ExecEndForeignScan              releases any resources allocated.
+ */
+#include "postgres.h"
+
+#include "executor/executor.h"
+#include "executor/nodeForeignscan.h"
+#include "foreign/fdwapi.h"
+
+static TupleTableSlot *ForeignNext(ForeignScanState *node);
+static bool ForeignRecheck(ForeignScanState *node, TupleTableSlot *slot);
+
+
+/* ----------------------------------------------------------------
+ *             ForeignNext
+ *
+ *             This is a workhorse for ExecForeignScan
+ * ----------------------------------------------------------------
+ */
+static TupleTableSlot *
+ForeignNext(ForeignScanState *node)
+{
+       TupleTableSlot *slot;
+       ForeignScan        *plan = (ForeignScan *) node->ss.ps.plan;
+       ExprContext *econtext = node->ss.ps.ps_ExprContext;
+       MemoryContext oldcontext;
+
+       /* Call the Iterate function in short-lived context */
+       oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
+       slot = node->fdwroutine->IterateForeignScan(node);
+       MemoryContextSwitchTo(oldcontext);
+
+       /*
+        * If any system columns are requested, we have to force the tuple into
+        * physical-tuple form to avoid "cannot extract system attribute from
+        * virtual tuple" errors later.  We also insert a valid value for
+        * tableoid, which is the only actually-useful system column.
+        */
+       if (plan->fsSystemCol && !TupIsNull(slot))
+       {
+               HeapTuple       tup = ExecMaterializeSlot(slot);
+
+               tup->t_tableOid = RelationGetRelid(node->ss.ss_currentRelation);
+       }
+
+       return slot;
+}
+
+/*
+ * ForeignRecheck -- access method routine to recheck a tuple in EvalPlanQual
+ */
+static bool
+ForeignRecheck(ForeignScanState *node, TupleTableSlot *slot)
+{
+       /* There are no access-method-specific conditions to recheck. */
+       return true;
+}
+
+/* ----------------------------------------------------------------
+ *             ExecForeignScan(node)
+ *
+ *             Fetches the next tuple from the FDW, checks local quals, and
+ *             returns it.
+ *             We call the ExecScan() routine and pass it the appropriate
+ *             access method functions.
+ * ----------------------------------------------------------------
+ */
+TupleTableSlot *
+ExecForeignScan(ForeignScanState *node)
+{
+       return ExecScan((ScanState *) node,
+                                       (ExecScanAccessMtd) ForeignNext,
+                                       (ExecScanRecheckMtd) ForeignRecheck);
+}
+
+
+/* ----------------------------------------------------------------
+ *             ExecInitForeignScan
+ * ----------------------------------------------------------------
+ */
+ForeignScanState *
+ExecInitForeignScan(ForeignScan *node, EState *estate, int eflags)
+{
+       ForeignScanState *scanstate;
+       Relation        currentRelation;
+       FdwRoutine *fdwroutine;
+
+       /* check for unsupported flags */
+       Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)));
+
+       /*
+        * create state structure
+        */
+       scanstate = makeNode(ForeignScanState);
+       scanstate->ss.ps.plan = (Plan *) node;
+       scanstate->ss.ps.state = estate;
+
+       /*
+        * Miscellaneous initialization
+        *
+        * create expression context for node
+        */
+       ExecAssignExprContext(estate, &scanstate->ss.ps);
+
+       scanstate->ss.ps.ps_TupFromTlist = false;
+
+       /*
+        * initialize child expressions
+        */
+       scanstate->ss.ps.targetlist = (List *)
+               ExecInitExpr((Expr *) node->scan.plan.targetlist,
+                                        (PlanState *) scanstate);
+       scanstate->ss.ps.qual = (List *)
+               ExecInitExpr((Expr *) node->scan.plan.qual,
+                                        (PlanState *) scanstate);
+
+       /*
+        * tuple table initialization
+        */
+       ExecInitResultTupleSlot(estate, &scanstate->ss.ps);
+       ExecInitScanTupleSlot(estate, &scanstate->ss);
+
+       /*
+        * open the base relation and acquire appropriate lock on it.
+        */
+       currentRelation = ExecOpenScanRelation(estate, node->scan.scanrelid);
+       scanstate->ss.ss_currentRelation = currentRelation;
+
+       /*
+        * get the scan type from the relation descriptor.
+        */
+       ExecAssignScanType(&scanstate->ss, RelationGetDescr(currentRelation));
+
+       /*
+        * Initialize result tuple type and projection info.
+        */
+       ExecAssignResultTypeFromTL(&scanstate->ss.ps);
+       ExecAssignScanProjectionInfo(&scanstate->ss);
+
+       /*
+        * Acquire function pointers from the FDW's handler, and init fdw_state.
+        */
+       fdwroutine = GetFdwRoutineByRelId(RelationGetRelid(currentRelation));
+       scanstate->fdwroutine = fdwroutine;
+       scanstate->fdw_state = NULL;
+
+       /*
+        * Tell the FDW to initiate the scan.
+        */
+       fdwroutine->BeginForeignScan(scanstate, eflags);
+
+       return scanstate;
+}
+
+/* ----------------------------------------------------------------
+ *             ExecEndForeignScan
+ *
+ *             frees any storage allocated through C routines.
+ * ----------------------------------------------------------------
+ */
+void
+ExecEndForeignScan(ForeignScanState *node)
+{
+       /* Let the FDW shut down */
+       node->fdwroutine->EndForeignScan(node);
+
+       /* Free the exprcontext */
+       ExecFreeExprContext(&node->ss.ps);
+
+       /* clean out the tuple table */
+       ExecClearTuple(node->ss.ps.ps_ResultTupleSlot);
+       ExecClearTuple(node->ss.ss_ScanTupleSlot);
+
+       /* close the relation. */
+       ExecCloseScanRelation(node->ss.ss_currentRelation);
+}
+
+/* ----------------------------------------------------------------
+ *             ExecReScanForeignScan
+ *
+ *             Rescans the relation.
+ * ----------------------------------------------------------------
+ */
+void
+ExecReScanForeignScan(ForeignScanState *node)
+{
+       node->fdwroutine->ReScanForeignScan(node);
+
+       ExecScanReScan(&node->ss);
+}
index 6e391025ffa6f522f3ae645c20d1602557464a6b..44cd18177c6f673469780f8a3fd9d215b43fac6d 100644 (file)
 #include "catalog/namespace.h"
 #include "catalog/pg_foreign_data_wrapper.h"
 #include "catalog/pg_foreign_server.h"
+#include "catalog/pg_foreign_table.h"
 #include "catalog/pg_type.h"
 #include "catalog/pg_user_mapping.h"
+#include "foreign/fdwapi.h"
 #include "foreign/foreign.h"
 #include "funcapi.h"
 #include "miscadmin.h"
@@ -54,19 +56,22 @@ GetForeignDataWrapper(Oid fdwid)
 
        fdwform = (Form_pg_foreign_data_wrapper) GETSTRUCT(tp);
 
-       fdw = palloc(sizeof(ForeignDataWrapper));
+       fdw = (ForeignDataWrapper *) palloc(sizeof(ForeignDataWrapper));
        fdw->fdwid = fdwid;
        fdw->owner = fdwform->fdwowner;
        fdw->fdwname = pstrdup(NameStr(fdwform->fdwname));
        fdw->fdwhandler = fdwform->fdwhandler;
        fdw->fdwvalidator = fdwform->fdwvalidator;
 
-       /* Extract the options */
+       /* Extract the fdwoptions */
        datum = SysCacheGetAttr(FOREIGNDATAWRAPPEROID,
                                                        tp,
                                                        Anum_pg_foreign_data_wrapper_fdwoptions,
                                                        &isnull);
-       fdw->options = untransformRelOptions(datum);
+       if (isnull)
+               fdw->options = NIL;
+       else
+               fdw->options = untransformRelOptions(datum);
 
        ReleaseSysCache(tp);
 
@@ -88,7 +93,8 @@ GetForeignDataWrapperOidByName(const char *fdwname, bool missing_ok)
        if (!OidIsValid(fdwId) && !missing_ok)
                ereport(ERROR,
                                (errcode(ERRCODE_UNDEFINED_OBJECT),
-                        errmsg("foreign-data wrapper \"%s\" does not exist", fdwname)));
+                                errmsg("foreign-data wrapper \"%s\" does not exist",
+                                               fdwname)));
 
        return fdwId;
 }
@@ -103,7 +109,7 @@ GetForeignDataWrapperByName(const char *fdwname, bool missing_ok)
 {
        Oid                     fdwId = GetForeignDataWrapperOidByName(fdwname, missing_ok);
 
-       if (!OidIsValid(fdwId) && missing_ok)
+       if (!OidIsValid(fdwId))
                return NULL;
 
        return GetForeignDataWrapper(fdwId);
@@ -129,7 +135,7 @@ GetForeignServer(Oid serverid)
 
        serverform = (Form_pg_foreign_server) GETSTRUCT(tp);
 
-       server = palloc(sizeof(ForeignServer));
+       server = (ForeignServer *) palloc(sizeof(ForeignServer));
        server->serverid = serverid;
        server->servername = pstrdup(NameStr(serverform->srvname));
        server->owner = serverform->srvowner;
@@ -154,9 +160,10 @@ GetForeignServer(Oid serverid)
                                                        tp,
                                                        Anum_pg_foreign_server_srvoptions,
                                                        &isnull);
-
-       /* untransformRelOptions does exactly what we want - avoid duplication */
-       server->options = untransformRelOptions(datum);
+       if (isnull)
+               server->options = NIL;
+       else
+               server->options = untransformRelOptions(datum);
 
        ReleaseSysCache(tp);
 
@@ -191,7 +198,7 @@ GetForeignServerByName(const char *srvname, bool missing_ok)
 {
        Oid                     serverid = GetForeignServerOidByName(srvname, missing_ok);
 
-       if (!OidIsValid(serverid) && missing_ok)
+       if (!OidIsValid(serverid))
                return NULL;
 
        return GetForeignServer(serverid);
@@ -233,16 +240,19 @@ GetUserMapping(Oid userid, Oid serverid)
 
        umform = (Form_pg_user_mapping) GETSTRUCT(tp);
 
+       um = (UserMapping *) palloc(sizeof(UserMapping));
+       um->userid = userid;
+       um->serverid = serverid;
+
        /* Extract the umoptions */
        datum = SysCacheGetAttr(USERMAPPINGUSERSERVER,
                                                        tp,
                                                        Anum_pg_user_mapping_umoptions,
                                                        &isnull);
-
-       um = palloc(sizeof(UserMapping));
-       um->userid = userid;
-       um->serverid = serverid;
-       um->options = untransformRelOptions(datum);
+       if (isnull)
+               um->options = NIL;
+       else
+               um->options = untransformRelOptions(datum);
 
        ReleaseSysCache(tp);
 
@@ -250,6 +260,116 @@ GetUserMapping(Oid userid, Oid serverid)
 }
 
 
+/*
+ * GetForeignTable - look up the foreign table definition by relation oid.
+ */
+ForeignTable *
+GetForeignTable(Oid relid)
+{
+       Form_pg_foreign_table tableform;
+       ForeignTable *ft;
+       HeapTuple       tp;
+       Datum           datum;
+       bool            isnull;
+
+       tp = SearchSysCache1(FOREIGNTABLEREL, ObjectIdGetDatum(relid));
+       if (!HeapTupleIsValid(tp))
+               elog(ERROR, "cache lookup failed for foreign table %u", relid);
+       tableform = (Form_pg_foreign_table) GETSTRUCT(tp);
+
+       ft = (ForeignTable *) palloc(sizeof(ForeignTable));
+       ft->relid = relid;
+       ft->serverid = tableform->ftserver;
+
+       /* Extract the ftoptions */
+       datum = SysCacheGetAttr(FOREIGNTABLEREL,
+                                                       tp,
+                                                       Anum_pg_foreign_table_ftoptions,
+                                                       &isnull);
+       if (isnull)
+               ft->options = NIL;
+       else
+               ft->options = untransformRelOptions(datum);
+
+       ReleaseSysCache(tp);
+
+       return ft;
+}
+
+
+/*
+ * GetFdwRoutine - call the specified foreign-data wrapper handler routine
+ * to get its FdwRoutine struct.
+ */
+FdwRoutine *
+GetFdwRoutine(Oid fdwhandler)
+{
+       Datum           datum;
+       FdwRoutine *routine;
+
+       datum = OidFunctionCall0(fdwhandler);
+       routine = (FdwRoutine *) DatumGetPointer(datum);
+
+       if (routine == NULL || !IsA(routine, FdwRoutine))
+               elog(ERROR, "foreign-data wrapper handler function %u did not return an FdwRoutine struct",
+                        fdwhandler);
+
+       return routine;
+}
+
+
+/*
+ * GetFdwRoutineByRelId - look up the handler of the foreign-data wrapper
+ * for the given foreign table, and retrieve its FdwRoutine struct.
+ */
+FdwRoutine *
+GetFdwRoutineByRelId(Oid relid)
+{
+       HeapTuple       tp;
+       Form_pg_foreign_data_wrapper fdwform;
+       Form_pg_foreign_server serverform;
+       Form_pg_foreign_table tableform;
+       Oid                     serverid;
+       Oid                     fdwid;
+       Oid                     fdwhandler;
+
+       /* Get server OID for the foreign table. */
+       tp = SearchSysCache1(FOREIGNTABLEREL, ObjectIdGetDatum(relid));
+       if (!HeapTupleIsValid(tp))
+               elog(ERROR, "cache lookup failed for foreign table %u", relid);
+       tableform = (Form_pg_foreign_table) GETSTRUCT(tp);
+       serverid = tableform->ftserver;
+       ReleaseSysCache(tp);
+
+       /* Get foreign-data wrapper OID for the server. */
+       tp = SearchSysCache1(FOREIGNSERVEROID, ObjectIdGetDatum(serverid));
+       if (!HeapTupleIsValid(tp))
+               elog(ERROR, "cache lookup failed for foreign server %u", serverid);
+       serverform = (Form_pg_foreign_server) GETSTRUCT(tp);
+       fdwid = serverform->srvfdw;
+       ReleaseSysCache(tp);
+
+       /* Get handler function OID for the FDW. */
+       tp = SearchSysCache1(FOREIGNDATAWRAPPEROID, ObjectIdGetDatum(fdwid));
+       if (!HeapTupleIsValid(tp))
+               elog(ERROR, "cache lookup failed for foreign-data wrapper %u", fdwid);
+       fdwform = (Form_pg_foreign_data_wrapper) GETSTRUCT(tp);
+       fdwhandler = fdwform->fdwhandler;
+
+       /* Complain if FDW has been set to NO HANDLER. */
+       if (!OidIsValid(fdwhandler))
+               ereport(ERROR,
+                               (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+                                errmsg("foreign-data wrapper \"%s\" has no handler",
+                                               NameStr(fdwform->fdwname))));
+
+       ReleaseSysCache(tp);
+
+       /* And finally, call the handler function. */
+       return GetFdwRoutine(fdwhandler);
+}
+
+
 /*
  * deflist_to_tuplestore - Helper function to convert DefElem list to
  * tuplestore usable in SRF.
@@ -261,7 +381,7 @@ deflist_to_tuplestore(ReturnSetInfo *rsinfo, List *options)
        TupleDesc       tupdesc;
        Tuplestorestate *tupstore;
        Datum           values[2];
-       bool            nulls[2] = {0};
+       bool            nulls[2];
        MemoryContext per_query_ctx;
        MemoryContext oldcontext;
 
@@ -294,6 +414,7 @@ deflist_to_tuplestore(ReturnSetInfo *rsinfo, List *options)
 
                values[0] = CStringGetTextDatum(def->defname);
                values[1] = CStringGetTextDatum(((Value *) def->arg)->val.str);
+               nulls[0] = nulls[1] = false;
                tuplestore_putvalues(tupstore, tupdesc, values, nulls);
        }
 
@@ -313,7 +434,8 @@ pg_options_to_table(PG_FUNCTION_ARGS)
 {
        Datum           array = PG_GETARG_DATUM(0);
 
-       deflist_to_tuplestore((ReturnSetInfo *) fcinfo->resultinfo, untransformRelOptions(array));
+       deflist_to_tuplestore((ReturnSetInfo *) fcinfo->resultinfo,
+                                                 untransformRelOptions(array));
 
        return (Datum) 0;
 }
@@ -407,7 +529,8 @@ postgresql_fdw_validator(PG_FUNCTION_ARGS)
                        ereport(ERROR,
                                        (errcode(ERRCODE_SYNTAX_ERROR),
                                         errmsg("invalid option \"%s\"", def->defname),
-                               errhint("Valid options in this context are: %s", buf.data)));
+                                        errhint("Valid options in this context are: %s",
+                                                        buf.data)));
 
                        PG_RETURN_BOOL(false);
                }
index 9a8a97c4bec1eef854df3815ca7ba6260fa4683c..1ba746bb4d893c0145e8376553c468d5b086fe1c 100644 (file)
@@ -23,6 +23,7 @@
 #include "postgres.h"
 
 #include "miscadmin.h"
+#include "foreign/fdwapi.h"
 #include "nodes/plannodes.h"
 #include "nodes/relation.h"
 #include "utils/datum.h"
@@ -549,6 +550,43 @@ _copyWorkTableScan(WorkTableScan *from)
        return newnode;
 }
 
+/*
+ * _copyForeignScan
+ */
+static ForeignScan *
+_copyForeignScan(ForeignScan *from)
+{
+       ForeignScan *newnode = makeNode(ForeignScan);
+
+       /*
+        * copy node superclass fields
+        */
+       CopyScanFields((Scan *) from, (Scan *) newnode);
+
+       /*
+        * copy remainder of node
+        */
+       COPY_SCALAR_FIELD(fsSystemCol);
+       COPY_NODE_FIELD(fdwplan);
+
+       return newnode;
+}
+
+/*
+ * _copyFdwPlan
+ */
+static FdwPlan *
+_copyFdwPlan(FdwPlan *from)
+{
+       FdwPlan *newnode = makeNode(FdwPlan);
+
+       COPY_SCALAR_FIELD(startup_cost);
+       COPY_SCALAR_FIELD(total_cost);
+       COPY_NODE_FIELD(fdw_private);
+
+       return newnode;
+}
+
 /*
  * CopyJoinFields
  *
@@ -3839,6 +3877,12 @@ copyObject(void *from)
                case T_WorkTableScan:
                        retval = _copyWorkTableScan(from);
                        break;
+               case T_ForeignScan:
+                       retval = _copyForeignScan(from);
+                       break;
+               case T_FdwPlan:
+                       retval = _copyFdwPlan(from);
+                       break;
                case T_Join:
                        retval = _copyJoin(from);
                        break;
index 192c04229137692be1e6bc35cdf21984bf924bb3..10f630e27f5b14f695540984feee31c096655ce2 100644 (file)
@@ -24,6 +24,7 @@
 #include <ctype.h>
 
 #include "lib/stringinfo.h"
+#include "foreign/fdwapi.h"
 #include "nodes/plannodes.h"
 #include "nodes/relation.h"
 #include "utils/datum.h"
@@ -537,6 +538,27 @@ _outWorkTableScan(StringInfo str, WorkTableScan *node)
        WRITE_INT_FIELD(wtParam);
 }
 
+static void
+_outForeignScan(StringInfo str, ForeignScan *node)
+{
+       WRITE_NODE_TYPE("FOREIGNSCAN");
+
+       _outScanInfo(str, (Scan *) node);
+
+       WRITE_BOOL_FIELD(fsSystemCol);
+       WRITE_NODE_FIELD(fdwplan);
+}
+
+static void
+_outFdwPlan(StringInfo str, FdwPlan *node)
+{
+       WRITE_NODE_TYPE("FDWPLAN");
+
+       WRITE_FLOAT_FIELD(startup_cost, "%.2f");
+       WRITE_FLOAT_FIELD(total_cost, "%.2f");
+       WRITE_NODE_FIELD(fdw_private);
+}
+
 static void
 _outJoin(StringInfo str, Join *node)
 {
@@ -1507,6 +1529,16 @@ _outTidPath(StringInfo str, TidPath *node)
        WRITE_NODE_FIELD(tidquals);
 }
 
+static void
+_outForeignPath(StringInfo str, ForeignPath *node)
+{
+       WRITE_NODE_TYPE("FOREIGNPATH");
+
+       _outPathInfo(str, (Path *) node);
+
+       WRITE_NODE_FIELD(fdwplan);
+}
+
 static void
 _outAppendPath(StringInfo str, AppendPath *node)
 {
@@ -2672,6 +2704,12 @@ _outNode(StringInfo str, void *obj)
                        case T_WorkTableScan:
                                _outWorkTableScan(str, obj);
                                break;
+                       case T_ForeignScan:
+                               _outForeignScan(str, obj);
+                               break;
+                       case T_FdwPlan:
+                               _outFdwPlan(str, obj);
+                               break;
                        case T_Join:
                                _outJoin(str, obj);
                                break;
@@ -2877,6 +2915,9 @@ _outNode(StringInfo str, void *obj)
                        case T_TidPath:
                                _outTidPath(str, obj);
                                break;
+                       case T_ForeignPath:
+                               _outForeignPath(str, obj);
+                               break;
                        case T_AppendPath:
                                _outAppendPath(str, obj);
                                break;
index 825be2cd3a6b8e39f7bbfbe20f4f5ae43e408ce7..aaa754cbb18e78823135bbc4a1acfbaa1f93c88f 100644 (file)
@@ -347,6 +347,7 @@ RelOptInfo      - a relation or joined relations
   IndexPath     - index scan
   BitmapHeapPath - top of a bitmapped index scan
   TidPath       - scan by CTID
+  ForeignPath   - scan a foreign table
   AppendPath    - append multiple subpaths together
   MergeAppendPath - merge multiple subpaths, preserving their common sort order
   ResultPath    - a Result plan node (used for FROM-less SELECT)
index 37d83323d652f31792e725dbe383a5719f6d1b47..c835a954ed97b29e87adb933af5078f27fc9971b 100644 (file)
@@ -17,6 +17,7 @@
 
 #include <math.h>
 
+#include "catalog/pg_class.h"
 #include "nodes/nodeFuncs.h"
 #ifdef OPTIMIZER_DEBUG
 #include "nodes/print.h"
@@ -34,6 +35,7 @@
 #include "parser/parse_clause.h"
 #include "parser/parsetree.h"
 #include "rewrite/rewriteManip.h"
+#include "utils/lsyscache.h"
 
 
 /* These parameters are set by GUC */
@@ -63,6 +65,8 @@ static void set_cte_pathlist(PlannerInfo *root, RelOptInfo *rel,
                                 RangeTblEntry *rte);
 static void set_worktable_pathlist(PlannerInfo *root, RelOptInfo *rel,
                                           RangeTblEntry *rte);
+static void set_foreign_pathlist(PlannerInfo *root, RelOptInfo *rel,
+                                          RangeTblEntry *rte);
 static RelOptInfo *make_rel_from_joinlist(PlannerInfo *root, List *joinlist);
 static bool subquery_is_pushdown_safe(Query *subquery, Query *topquery,
                                                  bool *differentTypes);
@@ -197,9 +201,17 @@ set_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
        }
        else
        {
-               /* Plain relation */
                Assert(rel->rtekind == RTE_RELATION);
-               set_plain_rel_pathlist(root, rel, rte);
+               if (get_rel_relkind(rte->relid) == RELKIND_FOREIGN_TABLE)
+               {
+                       /* Foreign table */
+                       set_foreign_pathlist(root, rel, rte);
+               }
+               else
+               {
+                       /* Plain relation */
+                       set_plain_rel_pathlist(root, rel, rte);
+               }
        }
 
 #ifdef OPTIMIZER_DEBUG
@@ -904,6 +916,23 @@ set_worktable_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
        set_cheapest(rel);
 }
 
+/*
+ * set_foreign_pathlist
+ *             Build the (single) access path for a foreign table RTE
+ */
+static void
+set_foreign_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
+{
+       /* Mark rel with estimated output rows, width, etc */
+       set_foreign_size_estimates(root, rel);
+
+       /* Generate appropriate path */
+       add_path(rel, (Path *) create_foreignscan_path(root, rel));
+
+       /* Select cheapest path (pretty easy in this case...) */
+       set_cheapest(rel);
+}
+
 /*
  * make_rel_from_joinlist
  *       Build access paths using a "joinlist" to guide the join path search.
@@ -1503,6 +1532,9 @@ print_path(PlannerInfo *root, Path *path, int indent)
                case T_TidPath:
                        ptype = "TidScan";
                        break;
+               case T_ForeignPath:
+                       ptype = "ForeignScan";
+                       break;
                case T_AppendPath:
                        ptype = "Append";
                        break;
index ffb066283f2176251fd3b9a5acb34395f5a422be..c292e24ac303ffce75cd2d0c4516be9360da4fa5 100644 (file)
@@ -3324,6 +3324,34 @@ set_cte_size_estimates(PlannerInfo *root, RelOptInfo *rel, Plan *cteplan)
        set_baserel_size_estimates(root, rel);
 }
 
+/*
+ * set_foreign_size_estimates
+ *             Set the size estimates for a base relation that is a foreign table.
+ *
+ * There is not a whole lot that we can do here; the foreign-data wrapper
+ * is responsible for producing useful estimates.  We can do a decent job
+ * of estimating baserestrictcost, so we set that, and we also set up width
+ * using what will be purely datatype-driven estimates from the targetlist.
+ * There is no way to do anything sane with the rows value, so we just put
+ * a default estimate and hope that the wrapper can improve on it.  The
+ * wrapper's PlanForeignScan function will be called momentarily.
+ *
+ * The rel's targetlist and restrictinfo list must have been constructed
+ * already.
+ */
+void
+set_foreign_size_estimates(PlannerInfo *root, RelOptInfo *rel)
+{
+       /* Should only be applied to base relations */
+       Assert(rel->relid > 0);
+
+       rel->rows = 1000;                       /* entirely bogus default estimate */
+
+       cost_qual_eval(&rel->baserestrictcost, rel->baserestrictinfo, root);
+
+       set_rel_width(root, rel);
+}
+
 
 /*
  * set_rel_width
index f01114c673a3104a3cc65afb9842c704bdb45d9e..4895858df60701778bba9222c33890119afa55e7 100644 (file)
@@ -20,6 +20,7 @@
 #include <math.h>
 
 #include "access/skey.h"
+#include "foreign/fdwapi.h"
 #include "miscadmin.h"
 #include "nodes/makefuncs.h"
 #include "nodes/nodeFuncs.h"
@@ -71,6 +72,8 @@ static CteScan *create_ctescan_plan(PlannerInfo *root, Path *best_path,
                                        List *tlist, List *scan_clauses);
 static WorkTableScan *create_worktablescan_plan(PlannerInfo *root, Path *best_path,
                                                  List *tlist, List *scan_clauses);
+static ForeignScan *create_foreignscan_plan(PlannerInfo *root, ForeignPath *best_path,
+                                               List *tlist, List *scan_clauses);
 static NestLoop *create_nestloop_plan(PlannerInfo *root, NestPath *best_path,
                                         Plan *outer_plan, Plan *inner_plan);
 static MergeJoin *create_mergejoin_plan(PlannerInfo *root, MergePath *best_path,
@@ -112,6 +115,8 @@ static CteScan *make_ctescan(List *qptlist, List *qpqual,
                         Index scanrelid, int ctePlanId, int cteParam);
 static WorkTableScan *make_worktablescan(List *qptlist, List *qpqual,
                                   Index scanrelid, int wtParam);
+static ForeignScan *make_foreignscan(List *qptlist, List *qpqual,
+                                Index scanrelid, bool fsSystemCol, FdwPlan *fdwplan);
 static BitmapAnd *make_bitmap_and(List *bitmapplans);
 static BitmapOr *make_bitmap_or(List *bitmapplans);
 static NestLoop *make_nestloop(List *tlist,
@@ -206,6 +211,7 @@ create_plan_recurse(PlannerInfo *root, Path *best_path)
                case T_ValuesScan:
                case T_CteScan:
                case T_WorkTableScan:
+               case T_ForeignScan:
                        plan = create_scan_plan(root, best_path);
                        break;
                case T_HashJoin:
@@ -346,6 +352,13 @@ create_scan_plan(PlannerInfo *root, Path *best_path)
                                                                                                          scan_clauses);
                        break;
 
+               case T_ForeignScan:
+                       plan = (Plan *) create_foreignscan_plan(root,
+                                                                                                       (ForeignPath *) best_path,
+                                                                                                       tlist,
+                                                                                                       scan_clauses);
+                       break;
+
                default:
                        elog(ERROR, "unrecognized node type: %d",
                                 (int) best_path->pathtype);
@@ -468,6 +481,7 @@ disuse_physical_tlist(Plan *plan, Path *path)
                case T_ValuesScan:
                case T_CteScan:
                case T_WorkTableScan:
+               case T_ForeignScan:
                        plan->targetlist = build_relation_tlist(path->parent);
                        break;
                default:
@@ -1755,6 +1769,56 @@ create_worktablescan_plan(PlannerInfo *root, Path *best_path,
        return scan_plan;
 }
 
+/*
+ * create_foreignscan_plan
+ *      Returns a foreignscan plan for the base relation scanned by 'best_path'
+ *      with restriction clauses 'scan_clauses' and targetlist 'tlist'.
+ */
+static ForeignScan *
+create_foreignscan_plan(PlannerInfo *root, ForeignPath *best_path,
+                                               List *tlist, List *scan_clauses)
+{
+       ForeignScan *scan_plan;
+       RelOptInfo *rel = best_path->path.parent;
+       Index           scan_relid = rel->relid;
+       RangeTblEntry *rte;
+       bool            fsSystemCol;
+       int                     i;
+
+       /* it should be a base rel... */
+       Assert(scan_relid > 0);
+       Assert(rel->rtekind == RTE_RELATION);
+       rte = planner_rt_fetch(scan_relid, root);
+       Assert(rte->rtekind == RTE_RELATION);
+
+       /* Sort clauses into best execution order */
+       scan_clauses = order_qual_clauses(root, scan_clauses);
+
+       /* Reduce RestrictInfo list to bare expressions; ignore pseudoconstants */
+       scan_clauses = extract_actual_clauses(scan_clauses, false);
+
+       /* Detect whether any system columns are requested from rel */
+       fsSystemCol = false;
+       for (i = rel->min_attr; i < 0; i++)
+       {
+               if (!bms_is_empty(rel->attr_needed[i - rel->min_attr]))
+               {
+                       fsSystemCol = true;
+                       break;
+               }
+       }
+
+       scan_plan = make_foreignscan(tlist,
+                                                                scan_clauses,
+                                                                scan_relid,
+                                                                fsSystemCol,
+                                                                best_path->fdwplan);
+
+       copy_path_costsize(&scan_plan->scan.plan, &best_path->path);
+
+       return scan_plan;
+}
+
 
 /*****************************************************************************
  *
@@ -2978,6 +3042,28 @@ make_worktablescan(List *qptlist,
        return node;
 }
 
+static ForeignScan *
+make_foreignscan(List *qptlist,
+                                List *qpqual,
+                                Index scanrelid,
+                                bool fsSystemCol,
+                                FdwPlan *fdwplan)
+{
+       ForeignScan *node = makeNode(ForeignScan);
+       Plan       *plan = &node->scan.plan;
+
+       /* cost should be inserted by caller */
+       plan->targetlist = qptlist;
+       plan->qual = qpqual;
+       plan->lefttree = NULL;
+       plan->righttree = NULL;
+       node->scan.scanrelid = scanrelid;
+       node->fsSystemCol = fsSystemCol;
+       node->fdwplan = fdwplan;
+
+       return node;
+}
+
 Append *
 make_append(List *appendplans, List *tlist)
 {
index 01b90383cf38bdc89aea57de46ac57e195846f9b..b73b872b3128f40b9a4bcfff01fd2e1aac20f62c 100644 (file)
@@ -1914,7 +1914,8 @@ preprocess_rowmarks(PlannerInfo *root)
                newrc->rti = newrc->prti = i;
                newrc->rowmarkId = ++(root->glob->lastRowMarkId);
                /* real tables support REFERENCE, anything else needs COPY */
-               if (rte->rtekind == RTE_RELATION)
+               if (rte->rtekind == RTE_RELATION &&
+                       get_rel_relkind(rte->relid) != RELKIND_FOREIGN_TABLE)
                        newrc->markType = ROW_MARK_REFERENCE;
                else
                        newrc->markType = ROW_MARK_COPY;
index d004f6cf12f57854baa043190588dc7cfdeb11bd..b1c181a1cc59e9cd74435dfc94b871175b2bd7b8 100644 (file)
@@ -402,6 +402,18 @@ set_plan_refs(PlannerGlobal *glob, Plan *plan, int rtoffset)
                                        fix_scan_list(glob, splan->scan.plan.qual, rtoffset);
                        }
                        break;
+               case T_ForeignScan:
+                       {
+                               ForeignScan *splan = (ForeignScan *) plan;
+
+                               splan->scan.scanrelid += rtoffset;
+                               splan->scan.plan.targetlist =
+                                       fix_scan_list(glob, splan->scan.plan.targetlist, rtoffset);
+                               splan->scan.plan.qual =
+                                       fix_scan_list(glob, splan->scan.plan.qual, rtoffset);
+                       }
+                       break;
+
                case T_NestLoop:
                case T_MergeJoin:
                case T_HashJoin:
index 29eb9dced455c2f929cde7498cbe2047e55aef29..96a257f6afa266a3ba4909112d368fa097c175ba 100644 (file)
@@ -2054,6 +2054,10 @@ finalize_plan(PlannerInfo *root, Plan *plan, Bitmapset *valid_params,
                        context.paramids = bms_add_members(context.paramids, scan_params);
                        break;
 
+               case T_ForeignScan:
+                       context.paramids = bms_add_members(context.paramids, scan_params);
+                       break;
+
                case T_ModifyTable:
                        {
                                ModifyTable *mtplan = (ModifyTable *) plan;
index d1010af66228ae18e5606478e09eca7b2ff8cd3c..1158f7715bcd29cd0039d1298c29206973148f79 100644 (file)
@@ -17,6 +17,7 @@
 #include <math.h>
 
 #include "catalog/pg_operator.h"
+#include "foreign/fdwapi.h"
 #include "miscadmin.h"
 #include "nodes/nodeFuncs.h"
 #include "optimizer/clauses.h"
@@ -1419,6 +1420,41 @@ create_worktablescan_path(PlannerInfo *root, RelOptInfo *rel)
        return pathnode;
 }
 
+/*
+ * create_foreignscan_path
+ *       Creates a path corresponding to a scan of a foreign table,
+ *       returning the pathnode.
+ */
+ForeignPath *
+create_foreignscan_path(PlannerInfo *root, RelOptInfo *rel)
+{
+       ForeignPath *pathnode = makeNode(ForeignPath);
+       RangeTblEntry *rte;
+       FdwRoutine *fdwroutine;
+       FdwPlan    *fdwplan;
+
+       pathnode->path.pathtype = T_ForeignScan;
+       pathnode->path.parent = rel;
+       pathnode->path.pathkeys = NIL;  /* result is always unordered */
+
+       /* Get FDW's callback info */
+       rte = planner_rt_fetch(rel->relid, root);
+       fdwroutine = GetFdwRoutineByRelId(rte->relid);
+
+       /* Let the FDW do its planning */
+       fdwplan = fdwroutine->PlanForeignScan(rte->relid, root, rel);
+       if (fdwplan == NULL || !IsA(fdwplan, FdwPlan))
+               elog(ERROR, "foreign-data wrapper PlanForeignScan function for relation %u did not return an FdwPlan struct",
+                        rte->relid);
+       pathnode->fdwplan = fdwplan;
+
+       /* use costs estimated by FDW */
+       pathnode->path.startup_cost = fdwplan->startup_cost;
+       pathnode->path.total_cost = fdwplan->total_cost;
+
+       return pathnode;
+}
+
 /*
  * create_nestloop_path
  *       Creates a pathnode corresponding to a nestloop join between two
index 40df626b62ff7c025117055923a203d3957b5fbf..b9c4cbe7f023277dff3697a35815d9ff4582ef53 100644 (file)
@@ -90,12 +90,6 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
         */
        relation = heap_open(relationObjectId, NoLock);
 
-       /* Foreign table scans are not implemented yet. */
-       if (relation->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
-               ereport(ERROR,
-                               (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-                                errmsg("foreign table scans are not yet supported")));
-
        rel->min_attr = FirstLowInvalidHeapAttributeNumber + 1;
        rel->max_attr = RelationGetNumberOfAttributes(relation);
        rel->reltablespace = RelationGetForm(relation)->reltablespace;
@@ -463,6 +457,11 @@ estimate_rel_size(Relation rel, int32 *attr_widths,
                        *pages = 1;
                        *tuples = 1;
                        break;
+               case RELKIND_FOREIGN_TABLE:
+                       /* Just use whatever's in pg_class */
+                       *pages = rel->rd_rel->relpages;
+                       *tuples = rel->rd_rel->reltuples;
+                       break;
                default:
                        /* else it has no disk storage; probably shouldn't get here? */
                        *pages = 0;
index 22447f92a2e1647d21e4ca0ec8f9eef4a45e826b..c7a7a230767889d332c037d866d42160e8e8f7f9 100644 (file)
@@ -40,6 +40,7 @@
 #include "parser/parse_target.h"
 #include "parser/parsetree.h"
 #include "rewrite/rewriteManip.h"
+#include "utils/lsyscache.h"
 #include "utils/rel.h"
 
 
@@ -2176,9 +2177,14 @@ transformLockingClause(ParseState *pstate, Query *qry, LockingClause *lc,
                        switch (rte->rtekind)
                        {
                                case RTE_RELATION:
-                                       applyLockingClause(qry, i,
-                                                                          lc->forUpdate, lc->noWait, pushedDown);
-                                       rte->requiredPerms |= ACL_SELECT_FOR_UPDATE;
+                                       /* ignore foreign tables */
+                                       if (get_rel_relkind(rte->relid) != RELKIND_FOREIGN_TABLE)
+                                       {
+                                               applyLockingClause(qry, i,
+                                                                                  lc->forUpdate, lc->noWait,
+                                                                                  pushedDown);
+                                               rte->requiredPerms |= ACL_SELECT_FOR_UPDATE;
+                                       }
                                        break;
                                case RTE_SUBQUERY:
                                        applyLockingClause(qry, i,
@@ -2225,6 +2231,12 @@ transformLockingClause(ParseState *pstate, Query *qry, LockingClause *lc,
                                        switch (rte->rtekind)
                                        {
                                                case RTE_RELATION:
+                                                       if (get_rel_relkind(rte->relid) == RELKIND_FOREIGN_TABLE)
+                                                               ereport(ERROR,
+                                                                               (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                                                                                errmsg("SELECT FOR UPDATE/SHARE cannot be used with foreign table \"%s\"",
+                                                                                               get_rel_name(rte->relid)),
+                                                                                parser_errposition(pstate, thisrel->location)));
                                                        applyLockingClause(qry, i,
                                                                                           lc->forUpdate, lc->noWait,
                                                                                           pushedDown);
index c6491e15194d53d57b169a24cc13e59edad458f0..3a50642fce8008d6b99b816515022cd10773d232 100644 (file)
@@ -1392,8 +1392,12 @@ markQueryForLocking(Query *qry, Node *jtnode,
 
                if (rte->rtekind == RTE_RELATION)
                {
-                       applyLockingClause(qry, rti, forUpdate, noWait, pushedDown);
-                       rte->requiredPerms |= ACL_SELECT_FOR_UPDATE;
+                       /* ignore foreign tables */
+                       if (get_rel_relkind(rte->relid) != RELKIND_FOREIGN_TABLE)
+                       {
+                               applyLockingClause(qry, rti, forUpdate, noWait, pushedDown);
+                               rte->requiredPerms |= ACL_SELECT_FOR_UPDATE;
+                       }
                }
                else if (rte->rtekind == RTE_SUBQUERY)
                {
index d05e4d2dd8f907e85a8400cf3a7ddb8a84cd18d3..a115a29da4c2cdd6339dfa5b51352b15cfa77ef7 100644 (file)
@@ -1621,6 +1621,26 @@ FunctionCall9(FmgrInfo *flinfo, Datum arg1, Datum arg2,
  * by FunctionCallN(). If the same function is to be invoked repeatedly,
  * do the fmgr_info() once and then use FunctionCallN().
  */
+Datum
+OidFunctionCall0(Oid functionId)
+{
+       FmgrInfo        flinfo;
+       FunctionCallInfoData fcinfo;
+       Datum           result;
+
+       fmgr_info(functionId, &flinfo);
+
+       InitFunctionCallInfoData(fcinfo, &flinfo, 0, NULL, NULL);
+
+       result = FunctionCallInvoke(&fcinfo);
+
+       /* Check for null result, since caller is clearly not expecting one */
+       if (fcinfo.isnull)
+               elog(ERROR, "function %u returned NULL", flinfo.fn_oid);
+
+       return result;
+}
+
 Datum
 OidFunctionCall1(Oid functionId, Datum arg1)
 {
index f17548338f8a6caf3a8b88e585bd8f083b4dcc22..2c38c92ae5124469a2c88ee741bc449264fa739f 100644 (file)
@@ -72,4 +72,15 @@ extern void ExplainBeginOutput(ExplainState *es);
 extern void ExplainEndOutput(ExplainState *es);
 extern void ExplainSeparatePlans(ExplainState *es);
 
+extern void ExplainPropertyList(const char *qlabel, List *data,
+                                                               ExplainState *es);
+extern void ExplainPropertyText(const char *qlabel, const char *value,
+                                                               ExplainState *es);
+extern void ExplainPropertyInteger(const char *qlabel, int value,
+                                                                  ExplainState *es);
+extern void ExplainPropertyLong(const char *qlabel, long value,
+                                                               ExplainState *es);
+extern void ExplainPropertyFloat(const char *qlabel, double value, int ndigits,
+                                                                ExplainState *es);
+
 #endif   /* EXPLAIN_H */
diff --git a/src/include/executor/nodeForeignscan.h b/src/include/executor/nodeForeignscan.h
new file mode 100644 (file)
index 0000000..5cfcfe7
--- /dev/null
@@ -0,0 +1,24 @@
+/*-------------------------------------------------------------------------
+ *
+ * nodeForeignscan.h
+ *
+ *
+ *
+ * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/executor/nodeForeignscan.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef NODEFOREIGNSCAN_H
+#define NODEFOREIGNSCAN_H
+
+#include "nodes/execnodes.h"
+
+extern ForeignScanState *ExecInitForeignScan(ForeignScan *node, EState *estate, int eflags);
+extern TupleTableSlot *ExecForeignScan(ForeignScanState *node);
+extern void ExecEndForeignScan(ForeignScanState *node);
+extern void ExecReScanForeignScan(ForeignScanState *node);
+
+#endif   /* NODEFOREIGNSCAN_H */
index 9720117a7f54aba6d1f403259ccffd23ec6fffd6..7539acace82db46549bd93e6c49dfdf9c2cff18a 100644 (file)
@@ -488,6 +488,7 @@ extern Datum FunctionCall9(FmgrInfo *flinfo, Datum arg1, Datum arg2,
  * by FunctionCallN(). If the same function is to be invoked repeatedly,
  * do the FunctionLookup() once and then use FunctionCallN().
  */
+extern Datum OidFunctionCall0(Oid functionId);
 extern Datum OidFunctionCall1(Oid functionId, Datum arg1);
 extern Datum OidFunctionCall2(Oid functionId, Datum arg1, Datum arg2);
 extern Datum OidFunctionCall3(Oid functionId, Datum arg1, Datum arg2,
diff --git a/src/include/foreign/fdwapi.h b/src/include/foreign/fdwapi.h
new file mode 100644 (file)
index 0000000..2873187
--- /dev/null
@@ -0,0 +1,98 @@
+/*-------------------------------------------------------------------------
+ *
+ * fdwapi.h
+ *       API for foreign-data wrappers
+ *
+ * Copyright (c) 2010-2011, PostgreSQL Global Development Group
+ *
+ * src/include/foreign/fdwapi.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef FDWAPI_H
+#define FDWAPI_H
+
+#include "nodes/execnodes.h"
+#include "nodes/relation.h"
+
+/* To avoid including explain.h here, reference ExplainState thus: */
+struct ExplainState;
+
+
+/*
+ * FdwPlan is the information returned to the planner by PlanForeignScan.
+ */
+typedef struct FdwPlan
+{
+       NodeTag         type;
+
+       /*
+        * Cost estimation info. The startup_cost is time before retrieving
+        * the first row, so it should include costs of connecting to the remote
+        * host, sending over the query, etc.  Note that PlanForeignScan also
+        * ought to set baserel->rows and baserel->width if it can produce any
+        * usable estimates of those values.
+        */
+       Cost            startup_cost;   /* cost expended before fetching any tuples */
+       Cost            total_cost;             /* total cost (assuming all tuples fetched) */
+
+       /*
+        * FDW private data, which will be available at execution time.
+        *
+        * Note that everything in this list must be copiable by copyObject().
+        * One way to store an arbitrary blob of bytes is to represent it as a
+        * bytea Const.  Usually, though, you'll be better off choosing a
+        * representation that can be dumped usefully by nodeToString().
+        */
+       List       *fdw_private;
+} FdwPlan;
+
+
+/*
+ * Callback function signatures --- see fdwhandler.sgml for more info.
+ */
+
+typedef FdwPlan * (*PlanForeignScan_function) (Oid foreigntableid,
+                                                                                          PlannerInfo *root,
+                                                                                          RelOptInfo *baserel);
+
+typedef void (*ExplainForeignScan_function) (ForeignScanState *node,
+                                                                                        struct ExplainState *es);
+
+typedef void (*BeginForeignScan_function) (ForeignScanState *node,
+                                                                                  int eflags);
+
+typedef TupleTableSlot * (*IterateForeignScan_function) (ForeignScanState *node);
+
+typedef void (*ReScanForeignScan_function) (ForeignScanState *node);
+
+typedef void (*EndForeignScan_function) (ForeignScanState *node);
+
+
+/*
+ * FdwRoutine is the struct returned by a foreign-data wrapper's handler
+ * function.  It provides pointers to the callback functions needed by the
+ * planner and executor.
+ *
+ * Currently, all functions must be supplied.  Later there may be optional
+ * additions.  It's recommended that the handler initialize the struct with
+ * makeNode(FdwRoutine) so that all fields are set to zero.
+ */
+typedef struct FdwRoutine
+{
+       NodeTag         type;
+
+       PlanForeignScan_function PlanForeignScan;
+       ExplainForeignScan_function ExplainForeignScan;
+       BeginForeignScan_function BeginForeignScan;
+       IterateForeignScan_function IterateForeignScan;
+       ReScanForeignScan_function ReScanForeignScan;
+       EndForeignScan_function EndForeignScan;
+} FdwRoutine;
+
+
+/* Functions in foreign/foreign.c */
+extern FdwRoutine *GetFdwRoutine(Oid fdwhandler);
+extern FdwRoutine *GetFdwRoutineByRelId(Oid relid);
+
+#endif   /* FDWAPI_H */
index 2cf0eaa09cc8c6515002dbe732e9a26df0878051..d676f3fce7451cbf880eb0c1c063a9250f20cb68 100644 (file)
@@ -60,6 +60,13 @@ typedef struct UserMapping
        List       *options;            /* useoptions as DefElem list */
 } UserMapping;
 
+typedef struct ForeignTable
+{
+       Oid                     relid;                  /* relation Oid */
+       Oid                     serverid;               /* server Oid */
+       List       *options;            /* ftoptions as DefElem list */
+} ForeignTable;
+
 
 extern ForeignServer *GetForeignServer(Oid serverid);
 extern ForeignServer *GetForeignServerByName(const char *name, bool missing_ok);
@@ -69,5 +76,6 @@ extern ForeignDataWrapper *GetForeignDataWrapper(Oid fdwid);
 extern ForeignDataWrapper *GetForeignDataWrapperByName(const char *name,
                                                        bool missing_ok);
 extern Oid     GetForeignDataWrapperOidByName(const char *name, bool missing_ok);
+extern ForeignTable *GetForeignTable(Oid relid);
 
 #endif   /* FOREIGN_H */
index 44da70ff60854458d8d617416c260ab4da410b97..0a6b829de4ff250c1eb64a8a476fba8e1ee9b449 100644 (file)
@@ -1403,6 +1403,20 @@ typedef struct WorkTableScanState
        RecursiveUnionState *rustate;
 } WorkTableScanState;
 
+/* ----------------
+ *      ForeignScanState information
+ *
+ *             ForeignScan nodes are used to scan foreign-data tables.
+ * ----------------
+ */
+typedef struct ForeignScanState
+{
+       ScanState       ss;                             /* its first field is NodeTag */
+       /* use struct pointer to avoid including fdwapi.h here */
+       struct FdwRoutine *fdwroutine;
+       void       *fdw_state;          /* foreign-data wrapper can keep state here */
+} ForeignScanState;
+
 /* ----------------------------------------------------------------
  *                              Join State Information
  * ----------------------------------------------------------------
index e0d05748dafb4d5c223bcc4cb2c17891b43f1d6e..cbaf123ee9d11a9914bd0df39cfeb86880e73778 100644 (file)
@@ -60,6 +60,8 @@ typedef enum NodeTag
        T_ValuesScan,
        T_CteScan,
        T_WorkTableScan,
+       T_ForeignScan,
+       T_FdwPlan,
        T_Join,
        T_NestLoop,
        T_MergeJoin,
@@ -103,6 +105,7 @@ typedef enum NodeTag
        T_ValuesScanState,
        T_CteScanState,
        T_WorkTableScanState,
+       T_ForeignScanState,
        T_JoinState,
        T_NestLoopState,
        T_MergeJoinState,
@@ -217,6 +220,7 @@ typedef enum NodeTag
        T_MergePath,
        T_HashPath,
        T_TidPath,
+       T_ForeignPath,
        T_AppendPath,
        T_MergeAppendPath,
        T_ResultPath,
@@ -409,7 +413,8 @@ typedef enum NodeTag
        T_ReturnSetInfo,                        /* in nodes/execnodes.h */
        T_WindowObjectData,                     /* private in nodeWindowAgg.c */
        T_TIDBitmap,                            /* in nodes/tidbitmap.h */
-       T_InlineCodeBlock                       /* in nodes/parsenodes.h */
+       T_InlineCodeBlock,                      /* in nodes/parsenodes.h */
+       T_FdwRoutine                            /* in foreign/fdwapi.h */
 } NodeTag;
 
 /*
index b4fb4f211b537777699b93c769e3b13a259658a3..90d61256e9c40297d9706e9b8178840f0806492b 100644 (file)
@@ -436,6 +436,18 @@ typedef struct WorkTableScan
        int                     wtParam;                /* ID of Param representing work table */
 } WorkTableScan;
 
+/* ----------------
+ *             ForeignScan node
+ * ----------------
+ */
+typedef struct ForeignScan
+{
+       Scan            scan;
+       bool            fsSystemCol;    /* true if any "system column" is needed */
+       /* use struct pointer to avoid including fdwapi.h here */
+       struct FdwPlan *fdwplan;
+} ForeignScan;
+
 
 /*
  * ==========
index 6f51b6c5fe403579788ec031bc60b9d6de7f0073..ab708351ed73b11305c64a74a317899b2c77e113 100644 (file)
@@ -749,6 +749,16 @@ typedef struct TidPath
        List       *tidquals;           /* qual(s) involving CTID = something */
 } TidPath;
 
+/*
+ * ForeignPath represents a scan of a foreign table
+ */
+typedef struct ForeignPath
+{
+       Path            path;
+       /* use struct pointer to avoid including fdwapi.h here */
+       struct FdwPlan *fdwplan;
+} ForeignPath;
+
 /*
  * AppendPath represents an Append plan, ie, successive execution of
  * several member plans.
index 3112bf75ccf67c776e03e2a590309121c513b6fb..e3cf7464df652bab3141a0903b38539801afc76b 100644 (file)
@@ -127,6 +127,7 @@ extern void set_function_size_estimates(PlannerInfo *root, RelOptInfo *rel);
 extern void set_values_size_estimates(PlannerInfo *root, RelOptInfo *rel);
 extern void set_cte_size_estimates(PlannerInfo *root, RelOptInfo *rel,
                                           Plan *cteplan);
+extern void set_foreign_size_estimates(PlannerInfo *root, RelOptInfo *rel);
 
 /*
  * prototypes for clausesel.c
index ff220e32a798928d128bd138638b5404b2b236d0..7a24da2c51cab2391e69f7f93287838a26ed2d21 100644 (file)
@@ -61,6 +61,7 @@ extern Path *create_functionscan_path(PlannerInfo *root, RelOptInfo *rel);
 extern Path *create_valuesscan_path(PlannerInfo *root, RelOptInfo *rel);
 extern Path *create_ctescan_path(PlannerInfo *root, RelOptInfo *rel);
 extern Path *create_worktablescan_path(PlannerInfo *root, RelOptInfo *rel);
+extern ForeignPath *create_foreignscan_path(PlannerInfo *root, RelOptInfo *rel);
 
 extern NestPath *create_nestloop_path(PlannerInfo *root,
                                         RelOptInfo *joinrel,
index 780098c2373548bdcb049b2e40c7fdf837e4aca1..00730f25cb83847e71878e9d517766bde1fc537b 100644 (file)
@@ -670,9 +670,9 @@ Has OIDs: no
 CREATE INDEX id_ft1_c2 ON ft1 (c2);                             -- ERROR
 ERROR:  "ft1" is not a table
 SELECT * FROM ft1;                                              -- ERROR
-ERROR:  foreign table scans are not yet supported
+ERROR:  foreign-data wrapper "dummy" has no handler
 EXPLAIN SELECT * FROM ft1;                                      -- ERROR
-ERROR:  foreign table scans are not yet supported
+ERROR:  foreign-data wrapper "dummy" has no handler
 -- ALTER FOREIGN TABLE
 COMMENT ON FOREIGN TABLE ft1 IS 'foreign table';
 COMMENT ON FOREIGN TABLE ft1 IS NULL;