</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>
--- /dev/null
+<!-- 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->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->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->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->rows</> is just a
+ constant default estimate, which should be replaced if at all possible.
+ The function may also choose to update <literal>baserel->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 & 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>
<!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">
&sources;
&nls;
&plhandler;
+ &fdwhandler;
&geqo;
&indexam;
&gist;
<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
<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>
#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"
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,
case T_WorkTableScan:
pname = sname = "WorkTable Scan";
break;
+ case T_ForeignScan:
+ pname = sname = "Foreign Scan";
+ break;
case T_Material:
pname = sname = "Materialize";
break;
case T_ValuesScan:
case T_CteScan:
case T_WorkTableScan:
+ case T_ForeignScan:
ExplainScanTarget((Scan *) plan, es);
break;
case T_BitmapIndexScan:
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);
}
}
+/*
+ * 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
*
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);
* 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;
}
}
+/*
+ * 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];
/*
* Explain a long-integer-valued property.
*/
-static void
+void
ExplainPropertyLong(const char *qlabel, long value, ExplainState *es)
{
char buf[32];
* 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)
{
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
#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"
ExecReScanWorkTableScan((WorkTableScanState *) node);
break;
+ case T_ForeignScanState:
+ ExecReScanForeignScan((ForeignScanState *) node);
+ break;
+
case T_NestLoopState:
ExecReScanNestLoop((NestLoopState *) node);
break;
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;
#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"
estate, eflags);
break;
+ case T_ForeignScan:
+ result = (PlanState *) ExecInitForeignScan((ForeignScan *) node,
+ estate, eflags);
+ break;
+
/*
* join nodes
*/
result = ExecWorkTableScan((WorkTableScanState *) node);
break;
+ case T_ForeignScanState:
+ result = ExecForeignScan((ForeignScanState *) node);
+ break;
+
/*
* join nodes
*/
ExecEndWorkTableScan((WorkTableScanState *) node);
break;
+ case T_ForeignScanState:
+ ExecEndForeignScan((ForeignScanState *) node);
+ break;
+
/*
* join nodes
*/
--- /dev/null
+/*-------------------------------------------------------------------------
+ *
+ * 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);
+}
#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"
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);
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;
}
{
Oid fdwId = GetForeignDataWrapperOidByName(fdwname, missing_ok);
- if (!OidIsValid(fdwId) && missing_ok)
+ if (!OidIsValid(fdwId))
return NULL;
return GetForeignDataWrapper(fdwId);
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;
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);
{
Oid serverid = GetForeignServerOidByName(srvname, missing_ok);
- if (!OidIsValid(serverid) && missing_ok)
+ if (!OidIsValid(serverid))
return NULL;
return GetForeignServer(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);
}
+/*
+ * 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.
TupleDesc tupdesc;
Tuplestorestate *tupstore;
Datum values[2];
- bool nulls[2] = {0};
+ bool nulls[2];
MemoryContext per_query_ctx;
MemoryContext oldcontext;
values[0] = CStringGetTextDatum(def->defname);
values[1] = CStringGetTextDatum(((Value *) def->arg)->val.str);
+ nulls[0] = nulls[1] = false;
tuplestore_putvalues(tupstore, tupdesc, values, nulls);
}
{
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;
}
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);
}
#include "postgres.h"
#include "miscadmin.h"
+#include "foreign/fdwapi.h"
#include "nodes/plannodes.h"
#include "nodes/relation.h"
#include "utils/datum.h"
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
*
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;
#include <ctype.h>
#include "lib/stringinfo.h"
+#include "foreign/fdwapi.h"
#include "nodes/plannodes.h"
#include "nodes/relation.h"
#include "utils/datum.h"
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)
{
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)
{
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;
case T_TidPath:
_outTidPath(str, obj);
break;
+ case T_ForeignPath:
+ _outForeignPath(str, obj);
+ break;
case T_AppendPath:
_outAppendPath(str, obj);
break;
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)
#include <math.h>
+#include "catalog/pg_class.h"
#include "nodes/nodeFuncs.h"
#ifdef OPTIMIZER_DEBUG
#include "nodes/print.h"
#include "parser/parse_clause.h"
#include "parser/parsetree.h"
#include "rewrite/rewriteManip.h"
+#include "utils/lsyscache.h"
/* These parameters are set by GUC */
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);
}
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
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.
case T_TidPath:
ptype = "TidScan";
break;
+ case T_ForeignPath:
+ ptype = "ForeignScan";
+ break;
case T_AppendPath:
ptype = "Append";
break;
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
#include <math.h>
#include "access/skey.h"
+#include "foreign/fdwapi.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
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,
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,
case T_ValuesScan:
case T_CteScan:
case T_WorkTableScan:
+ case T_ForeignScan:
plan = create_scan_plan(root, best_path);
break;
case T_HashJoin:
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);
case T_ValuesScan:
case T_CteScan:
case T_WorkTableScan:
+ case T_ForeignScan:
plan->targetlist = build_relation_tlist(path->parent);
break;
default:
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;
+}
+
/*****************************************************************************
*
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)
{
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;
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:
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;
#include <math.h>
#include "catalog/pg_operator.h"
+#include "foreign/fdwapi.h"
#include "miscadmin.h"
#include "nodes/nodeFuncs.h"
#include "optimizer/clauses.h"
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
*/
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;
*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;
#include "parser/parse_target.h"
#include "parser/parsetree.h"
#include "rewrite/rewriteManip.h"
+#include "utils/lsyscache.h"
#include "utils/rel.h"
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,
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);
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)
{
* 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)
{
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 */
--- /dev/null
+/*-------------------------------------------------------------------------
+ *
+ * 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 */
* 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,
--- /dev/null
+/*-------------------------------------------------------------------------
+ *
+ * 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 */
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);
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 */
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
* ----------------------------------------------------------------
T_ValuesScan,
T_CteScan,
T_WorkTableScan,
+ T_ForeignScan,
+ T_FdwPlan,
T_Join,
T_NestLoop,
T_MergeJoin,
T_ValuesScanState,
T_CteScanState,
T_WorkTableScanState,
+ T_ForeignScanState,
T_JoinState,
T_NestLoopState,
T_MergeJoinState,
T_MergePath,
T_HashPath,
T_TidPath,
+ T_ForeignPath,
T_AppendPath,
T_MergeAppendPath,
T_ResultPath,
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;
/*
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;
+
/*
* ==========
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.
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
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,
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;