mirror of https://github.com/postgres/postgres
This commit provides the core code and documentation needed. A contrib module test case will follow shortly. Shigeru Hanada, Jan Urbanski, Heikki Linnakangaspull/1/head
parent
d5813488a4
commit
bb74240794
@ -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->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> |
@ -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); |
||||
} |
@ -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 */ |
@ -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 */ |
Loading…
Reference in new issue