mirror of
https://github.com/postgres/postgres.git
synced 2025-07-05 07:21:24 +03:00
Implement an API to let foreign-data wrappers actually be functional.
This commit provides the core code and documentation needed. A contrib module test case will follow shortly. Shigeru Hanada, Jan Urbanski, Heikki Linnakangas
This commit is contained in:
@ -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)
|
||||
{
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
*/
|
||||
|
209
src/backend/executor/nodeForeignscan.c
Normal file
209
src/backend/executor/nodeForeignscan.c
Normal file
@ -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);
|
||||
}
|
@ -16,8 +16,10 @@
|
||||
#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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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;
|
||||
|
@ -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:
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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)
|
||||
{
|
||||
|
Reference in New Issue
Block a user