contrib/file_fdw/file_fdw.c | 177 +++++++++++++++++++++++++++++++- src/backend/commands/trigger.c | 12 ++- src/backend/executor/execMain.c | 34 +++++- src/backend/executor/nodeForeignscan.c | 136 +++++++++++++++++++++++- src/backend/executor/nodeModifyTable.c | 142 ++++++++++++++++++++----- src/backend/nodes/copyfuncs.c | 1 + src/backend/nodes/outfuncs.c | 1 + src/backend/optimizer/plan/createplan.c | 19 ++++ src/backend/optimizer/plan/initsplan.c | 10 +- src/backend/optimizer/plan/planmain.c | 2 +- src/backend/optimizer/plan/planner.c | 2 +- src/backend/optimizer/prep/prepunion.c | 3 +- src/backend/optimizer/util/plancat.c | 23 ++++- src/backend/optimizer/util/relnode.c | 7 +- src/backend/rewrite/rewriteHandler.c | 55 ++++++++-- src/include/commands/trigger.h | 8 +- src/include/foreign/fdwapi.h | 25 +++++ src/include/nodes/execnodes.h | 13 ++- src/include/nodes/plannodes.h | 1 + src/include/optimizer/pathnode.h | 2 +- src/include/optimizer/plancat.h | 2 +- src/include/optimizer/planmain.h | 3 +- 22 files changed, 609 insertions(+), 69 deletions(-) diff --git a/contrib/file_fdw/file_fdw.c b/contrib/file_fdw/file_fdw.c index 81fc4e2..36ffe44 100644 --- a/contrib/file_fdw/file_fdw.c +++ b/contrib/file_fdw/file_fdw.c @@ -34,6 +34,7 @@ #include "optimizer/var.h" #include "utils/memutils.h" #include "utils/rel.h" +#include "utils/lsyscache.h" PG_MODULE_MAGIC; @@ -87,6 +88,7 @@ typedef struct FileFdwPlanState List *options; /* merged COPY options, excluding filename */ BlockNumber pages; /* estimate of file's physical size */ double ntuples; /* estimate of number of rows in file */ + DefElem *anum_rowid; /* attribute number of rowid */ } FileFdwPlanState; /* @@ -97,6 +99,8 @@ typedef struct FileFdwExecutionState char *filename; /* file to read */ List *options; /* merged COPY options, excluding filename */ CopyState cstate; /* state of reading file */ + int lineno; /* pseudo ctid of the file */ + AttrNumber anum_rowid; /* attribute number of rowid */ } FileFdwExecutionState; /* @@ -111,6 +115,10 @@ PG_FUNCTION_INFO_V1(file_fdw_validator); /* * FDW callback routines */ +static void fileGetForeignRelInfo(PlannerInfo *root, + RelOptInfo *baserel, + Oid foreigntableid, + bool inhparent, List *targetList); static void fileGetForeignRelSize(PlannerInfo *root, RelOptInfo *baserel, Oid foreigntableid); @@ -131,6 +139,19 @@ static void fileEndForeignScan(ForeignScanState *node); static bool fileAnalyzeForeignTable(Relation relation, AcquireSampleRowsFunc *func, BlockNumber *totalpages); +static void fileBeginForeignModify(CmdType operation, + ResultRelInfo *resultRelInfo, + EState *estate, + Plan *subplan, + int eflags); +static int fileExecForeignInsert(ResultRelInfo *resultRelInfo, + HeapTuple tuple); +static int fileExecForeignDelete(ResultRelInfo *resultRelInfo, + Datum rowid); +static int fileExecForeignUpdate(ResultRelInfo *resultRelInfo, + Datum rowid, + HeapTuple tuple); +static void fileEndForeignModify(ResultRelInfo *resultRelInfo); /* * Helper functions @@ -169,7 +190,13 @@ file_fdw_handler(PG_FUNCTION_ARGS) fdwroutine->IterateForeignScan = fileIterateForeignScan; fdwroutine->ReScanForeignScan = fileReScanForeignScan; fdwroutine->EndForeignScan = fileEndForeignScan; + fdwroutine->GetForeignRelInfo = fileGetForeignRelInfo; fdwroutine->AnalyzeForeignTable = fileAnalyzeForeignTable; + fdwroutine->BeginForeignModify = fileBeginForeignModify; + fdwroutine->ExecForeignInsert = fileExecForeignInsert; + fdwroutine->ExecForeignDelete = fileExecForeignDelete; + fdwroutine->ExecForeignUpdate = fileExecForeignUpdate; + fdwroutine->EndForeignModify = fileEndForeignModify; PG_RETURN_POINTER(fdwroutine); } @@ -424,6 +451,47 @@ get_file_fdw_attribute_options(Oid relid) } /* + * fileGetForeignRelInfo + * Adjust size of baserel->attr_needed according to var references + * within targetList. + */ +static void +fileGetForeignRelInfo(PlannerInfo *root, + RelOptInfo *baserel, + Oid foreigntableid, + bool inhparent, List *targetList) +{ + FileFdwPlanState *fdw_private; + ListCell *cell; + + fdw_private = (FileFdwPlanState *) palloc0(sizeof(FileFdwPlanState)); + + foreach(cell, targetList) + { + TargetEntry *tle = lfirst(cell); + + if (tle->resjunk && + tle->resname && strcmp(tle->resname, "rowid")==0) + { + Bitmapset *temp = NULL; + AttrNumber anum_rowid; + DefElem *defel; + + pull_varattnos((Node *)tle, baserel->relid, &temp); + anum_rowid = bms_singleton_member(temp) + + FirstLowInvalidHeapAttributeNumber; + /* adjust attr_needed of baserel */ + if (anum_rowid > baserel->max_attr) + baserel->max_attr = anum_rowid; + defel = makeDefElem("anum_rowid", + (Node *)makeInteger(anum_rowid)); + fdw_private->anum_rowid = defel; + } + } + baserel->fdw_private = fdw_private; +} + +/* * fileGetForeignRelSize * Obtain relation size estimates for a foreign table */ @@ -432,16 +500,14 @@ fileGetForeignRelSize(PlannerInfo *root, RelOptInfo *baserel, Oid foreigntableid) { - FileFdwPlanState *fdw_private; + FileFdwPlanState *fdw_private = (FileFdwPlanState *) baserel->fdw_private; /* * Fetch options. We only need filename at this point, but we might as * well get everything and not need to re-fetch it later in planning. */ - fdw_private = (FileFdwPlanState *) palloc(sizeof(FileFdwPlanState)); fileGetOptions(foreigntableid, &fdw_private->filename, &fdw_private->options); - baserel->fdw_private = (void *) fdw_private; /* Estimate relation size */ estimate_size(root, baserel, fdw_private); @@ -473,6 +539,10 @@ fileGetForeignPaths(PlannerInfo *root, coptions = list_make1(makeDefElem("convert_selectively", (Node *) columns)); + /* save attribute number of rowid pseudo column */ + if (fdw_private->anum_rowid) + coptions = lappend(coptions, fdw_private->anum_rowid); + /* Estimate costs */ estimate_costs(root, baserel, fdw_private, &startup_cost, &total_cost); @@ -513,6 +583,35 @@ fileGetForeignPlan(PlannerInfo *root, Index scan_relid = baserel->relid; /* + * XXX - An evidence FDW module can know what kind of accesses are + * requires on the target relation, if UPDATE, INSERT or DELETE. + * It shall be also utilized to appropriate lock level on FDW + * extensions that performs behalf on real RDBMS. + */ + if (root->parse->resultRelation == baserel->relid) + { + switch (root->parse->commandType) + { + case CMD_INSERT: + elog(INFO, "%s is the target relation of INSERT", + get_rel_name(foreigntableid)); + break; + case CMD_UPDATE: + elog(INFO, "%s is the target relation of UPDATE", + get_rel_name(foreigntableid)); + break; + case CMD_DELETE: + elog(INFO, "%s is the target relation of DELETE", + get_rel_name(foreigntableid)); + break; + default: + elog(INFO, "%s is the target relation of ??????", + get_rel_name(foreigntableid)); + break; + } + } + + /* * We have no native ability to evaluate restriction clauses, so we just * put all the scan_clauses into the plan node's qual list for the * executor to check. So all we have to do here is strip RestrictInfo @@ -566,7 +665,10 @@ fileBeginForeignScan(ForeignScanState *node, int eflags) ForeignScan *plan = (ForeignScan *) node->ss.ps.plan; char *filename; List *options; + ListCell *cell; + ListCell *prev; CopyState cstate; + AttrNumber anum_rowid = InvalidAttrNumber; FileFdwExecutionState *festate; /* @@ -582,6 +684,21 @@ fileBeginForeignScan(ForeignScanState *node, int eflags) /* Add any options from the plan (currently only convert_selectively) */ options = list_concat(options, plan->fdw_private); + /* Fetch anum_rowid, if exist in options */ + prev = NULL; + foreach (cell, options) + { + DefElem *defel = lfirst(cell); + + if (strcmp(defel->defname, "anum_rowid") == 0) + { + anum_rowid = intVal(defel->arg); + options = list_delete_cell(options, cell, prev); + break; + } + prev = cell; + } + /* * Create CopyState from FDW options. We always acquire all columns, so * as to match the expected ScanTupleSlot signature. @@ -599,6 +716,8 @@ fileBeginForeignScan(ForeignScanState *node, int eflags) festate->filename = filename; festate->options = options; festate->cstate = cstate; + festate->anum_rowid = anum_rowid; + festate->lineno = 1; node->fdw_state = (void *) festate; } @@ -639,7 +758,16 @@ fileIterateForeignScan(ForeignScanState *node) slot->tts_values, slot->tts_isnull, NULL); if (found) + { + if (festate->anum_rowid != InvalidAttrNumber) + { + slot->tts_values[festate->anum_rowid - 1] + = Int32GetDatum(festate->lineno); + slot->tts_isnull[festate->anum_rowid - 1] = false; + } ExecStoreVirtualTuple(slot); + festate->lineno++; + } /* Remove error callback. */ error_context_stack = errcontext.previous; @@ -662,6 +790,7 @@ fileReScanForeignScan(ForeignScanState *node) festate->filename, NIL, festate->options); + festate->lineno = 1; } /* @@ -717,6 +846,44 @@ fileAnalyzeForeignTable(Relation relation, return true; } +static void +fileBeginForeignModify(CmdType operation, + ResultRelInfo *resultRelInfo, + EState *estate, + Plan *subplan, + int eflags) +{ + elog(INFO, "fdw_file: BeginForeignModify method"); +} + +static int +fileExecForeignInsert(ResultRelInfo *resultRelInfo, HeapTuple tuple) +{ + elog(INFO, "fdw_file: INSERT"); + return 1; +} + +static int +fileExecForeignDelete(ResultRelInfo *resultRelInfo, Datum rowid) +{ + elog(INFO, "fdw_file: DELETE (lineno = %u)", DatumGetInt32(rowid)); + return 1; +} + +static int +fileExecForeignUpdate(ResultRelInfo *resultRelInfo, + Datum rowid, HeapTuple tuple) +{ + elog(INFO, "fdw_file: UPDATE (lineno = %u)", DatumGetInt32(rowid)); + return 1; +} + +static void +fileEndForeignModify(ResultRelInfo *resultRelInfo) +{ + elog(INFO, "fdw_file: EndForeignModify method"); +} + /* * check_selective_binary_conversion * @@ -789,8 +956,8 @@ check_selective_binary_conversion(RelOptInfo *baserel, break; } - /* Ignore system attributes. */ - if (attnum < 0) + /* Ignore system or pseudo attributes. */ + if (attnum < 0 || attnum > RelationGetNumberOfAttributes(rel)) continue; /* Get user attributes. */ diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c index 4d3ed9c..c67c051 100644 --- a/src/backend/commands/trigger.c +++ b/src/backend/commands/trigger.c @@ -2132,9 +2132,10 @@ ExecASDeleteTriggers(EState *estate, ResultRelInfo *relinfo) bool ExecBRDeleteTriggers(EState *estate, EPQState *epqstate, ResultRelInfo *relinfo, - ItemPointer tupleid) + Datum rowid) { TriggerDesc *trigdesc = relinfo->ri_TrigDesc; + ItemPointer tupleid = (ItemPointer)DatumGetPointer(rowid); bool result = true; TriggerData LocTriggerData; HeapTuple trigtuple; @@ -2190,9 +2191,10 @@ ExecBRDeleteTriggers(EState *estate, EPQState *epqstate, void ExecARDeleteTriggers(EState *estate, ResultRelInfo *relinfo, - ItemPointer tupleid) + Datum rowid) { TriggerDesc *trigdesc = relinfo->ri_TrigDesc; + ItemPointer tupleid = (ItemPointer)DatumGetPointer(rowid); if (trigdesc && trigdesc->trig_delete_after_row) { @@ -2317,11 +2319,12 @@ ExecASUpdateTriggers(EState *estate, ResultRelInfo *relinfo) TupleTableSlot * ExecBRUpdateTriggers(EState *estate, EPQState *epqstate, ResultRelInfo *relinfo, - ItemPointer tupleid, TupleTableSlot *slot) + Datum rowid, TupleTableSlot *slot) { TriggerDesc *trigdesc = relinfo->ri_TrigDesc; HeapTuple slottuple = ExecMaterializeSlot(slot); HeapTuple newtuple = slottuple; + ItemPointer tupleid = (ItemPointer)DatumGetPointer(rowid); TriggerData LocTriggerData; HeapTuple trigtuple; HeapTuple oldtuple; @@ -2414,10 +2417,11 @@ ExecBRUpdateTriggers(EState *estate, EPQState *epqstate, void ExecARUpdateTriggers(EState *estate, ResultRelInfo *relinfo, - ItemPointer tupleid, HeapTuple newtuple, + Datum rowid, HeapTuple newtuple, List *recheckIndexes) { TriggerDesc *trigdesc = relinfo->ri_TrigDesc; + ItemPointer tupleid = (ItemPointer)DatumGetPointer(rowid); if (trigdesc && trigdesc->trig_update_after_row) { diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index d966be5..c701fe7 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -44,6 +44,7 @@ #include "catalog/namespace.h" #include "commands/trigger.h" #include "executor/execdebug.h" +#include "foreign/fdwapi.h" #include "mb/pg_wchar.h" #include "miscadmin.h" #include "optimizer/clauses.h" @@ -934,6 +935,7 @@ void CheckValidResultRel(Relation resultRel, CmdType operation) { TriggerDesc *trigDesc = resultRel->trigdesc; + FdwRoutine *fdwroutine; switch (resultRel->rd_rel->relkind) { @@ -985,10 +987,34 @@ CheckValidResultRel(Relation resultRel, CmdType operation) } break; case RELKIND_FOREIGN_TABLE: - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("cannot change foreign table \"%s\"", - RelationGetRelationName(resultRel)))); + fdwroutine = GetFdwRoutineByRelId(RelationGetRelid(resultRel)); + switch (operation) + { + case CMD_INSERT: + if (!fdwroutine || !fdwroutine->ExecForeignInsert) + ereport(ERROR, + (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), + errmsg("cannot insert into foreign table \"%s\"", + RelationGetRelationName(resultRel)))); + break; + case CMD_UPDATE: + if (!fdwroutine || !fdwroutine->ExecForeignUpdate) + ereport(ERROR, + (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), + errmsg("cannot update foreign table \"%s\"", + RelationGetRelationName(resultRel)))); + break; + case CMD_DELETE: + if (!fdwroutine || !fdwroutine->ExecForeignDelete) + ereport(ERROR, + (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), + errmsg("cannot delete from foreign table \"%s\"", + RelationGetRelationName(resultRel)))); + break; + default: + elog(ERROR, "unrecognized CmdType: %d", (int) operation); + break; + } break; default: ereport(ERROR, diff --git a/src/backend/executor/nodeForeignscan.c b/src/backend/executor/nodeForeignscan.c index 9204859..1ee626f 100644 --- a/src/backend/executor/nodeForeignscan.c +++ b/src/backend/executor/nodeForeignscan.c @@ -25,6 +25,7 @@ #include "executor/executor.h" #include "executor/nodeForeignscan.h" #include "foreign/fdwapi.h" +#include "nodes/nodeFuncs.h" #include "utils/rel.h" static TupleTableSlot *ForeignNext(ForeignScanState *node); @@ -93,6 +94,133 @@ ExecForeignScan(ForeignScanState *node) (ExecScanRecheckMtd) ForeignRecheck); } +/* + * pseudo_column_walker + * + * helper routine of GetPseudoTupleDesc. It pulls Var nodes that reference + * pseudo columns from targetlis of the relation + */ +typedef struct +{ + Relation relation; + Index varno; + List *pcolumns; + AttrNumber max_attno; +} pseudo_column_walker_context; + +static bool +pseudo_column_walker(Node *node, pseudo_column_walker_context *context) +{ + if (node == NULL) + return false; + if (IsA(node, Var)) + { + Var *var = (Var *) node; + ListCell *cell; + + if (var->varno == context->varno && var->varlevelsup == 0 && + var->varattno > RelationGetNumberOfAttributes(context->relation)) + { + foreach (cell, context->pcolumns) + { + Var *temp = lfirst(cell); + + if (temp->varattno == var->varattno) + { + if (!equal(var, temp)) + elog(ERROR, "asymmetric pseudo column appeared"); + break; + } + } + if (!cell) + { + context->pcolumns = lappend(context->pcolumns, var); + if (var->varattno > context->max_attno) + context->max_attno = var->varattno; + } + } + return false; + } + + /* Should not find an unplanned subquery */ + Assert(!IsA(node, Query)); + + return expression_tree_walker(node, pseudo_column_walker, + (void *)context); +} + +/* + * GetPseudoTupleDesc + * + * It generates TupleDesc structure including pseudo-columns if required. + */ +static TupleDesc +GetPseudoTupleDesc(ForeignScan *node, Relation relation) +{ + pseudo_column_walker_context context; + List *target_list = node->scan.plan.targetlist; + TupleDesc tupdesc; + AttrNumber attno; + ListCell *cell; + ListCell *prev; + bool hasoid; + + context.relation = relation; + context.varno = node->scan.scanrelid; + context.pcolumns = NIL; + context.max_attno = -1; + + pseudo_column_walker((Node *)target_list, (void *)&context); + Assert(context.max_attno > RelationGetNumberOfAttributes(relation)); + + hasoid = RelationGetForm(relation)->relhasoids; + tupdesc = CreateTemplateTupleDesc(context.max_attno, hasoid); + + for (attno = 1; attno <= context.max_attno; attno++) + { + /* case of regular columns */ + if (attno <= RelationGetNumberOfAttributes(relation)) + { + memcpy(tupdesc->attrs[attno - 1], + RelationGetDescr(relation)->attrs[attno - 1], + ATTRIBUTE_FIXED_PART_SIZE); + continue; + } + + /* case of pseudo columns */ + prev = NULL; + foreach (cell, context.pcolumns) + { + Var *var = lfirst(cell); + + if (var->varattno == attno) + { + char namebuf[NAMEDATALEN]; + + snprintf(namebuf, sizeof(namebuf), + "pseudo_column_%d", attno); + + TupleDescInitEntry(tupdesc, + attno, + namebuf, + var->vartype, + var->vartypmod, + 0); + TupleDescInitEntryCollation(tupdesc, + attno, + var->varcollid); + context.pcolumns + = list_delete_cell(context.pcolumns, cell, prev); + break; + } + prev = cell; + } + if (!cell) + elog(ERROR, "pseudo column %d of %s not in target list", + attno, RelationGetRelationName(relation)); + } + return tupdesc; +} /* ---------------------------------------------------------------- * ExecInitForeignScan @@ -103,6 +231,7 @@ ExecInitForeignScan(ForeignScan *node, EState *estate, int eflags) { ForeignScanState *scanstate; Relation currentRelation; + TupleDesc tupdesc; FdwRoutine *fdwroutine; /* check for unsupported flags */ @@ -149,7 +278,12 @@ ExecInitForeignScan(ForeignScan *node, EState *estate, int eflags) /* * get the scan type from the relation descriptor. */ - ExecAssignScanType(&scanstate->ss, RelationGetDescr(currentRelation)); + if (node->fsPseudoCol) + tupdesc = GetPseudoTupleDesc(node, currentRelation); + else + tupdesc = RelationGetDescr(currentRelation); + + ExecAssignScanType(&scanstate->ss, tupdesc); /* * Initialize result tuple type and projection info. diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c index 26a59d0..97a0d82 100644 --- a/src/backend/executor/nodeModifyTable.c +++ b/src/backend/executor/nodeModifyTable.c @@ -42,6 +42,7 @@ #include "commands/trigger.h" #include "executor/executor.h" #include "executor/nodeModifyTable.h" +#include "foreign/fdwapi.h" #include "miscadmin.h" #include "nodes/nodeFuncs.h" #include "storage/bufmgr.h" @@ -170,6 +171,7 @@ ExecInsert(TupleTableSlot *slot, Relation resultRelationDesc; Oid newId; List *recheckIndexes = NIL; + int num_rows = 1; /* * get the heap tuple out of the tuple table slot, making sure we have a @@ -225,6 +227,14 @@ ExecInsert(TupleTableSlot *slot, newId = InvalidOid; } + else if (resultRelInfo->ri_fdwroutine) + { + FdwRoutine *fdwroutine = resultRelInfo->ri_fdwroutine; + + num_rows = fdwroutine->ExecForeignInsert(resultRelInfo, tuple); + + newId = InvalidOid; + } else { /* @@ -252,7 +262,7 @@ ExecInsert(TupleTableSlot *slot, if (canSetTag) { - (estate->es_processed)++; + (estate->es_processed) += num_rows; estate->es_lastoid = newId; setLastTid(&(tuple->t_self)); } @@ -285,7 +295,7 @@ ExecInsert(TupleTableSlot *slot, * ---------------------------------------------------------------- */ static TupleTableSlot * -ExecDelete(ItemPointer tupleid, +ExecDelete(Datum rowid, HeapTupleHeader oldtuple, TupleTableSlot *planSlot, EPQState *epqstate, @@ -297,6 +307,7 @@ ExecDelete(ItemPointer tupleid, HTSU_Result result; ItemPointerData update_ctid; TransactionId update_xmax; + int num_rows = 1; /* * get information on the (current) result relation @@ -311,7 +322,7 @@ ExecDelete(ItemPointer tupleid, bool dodelete; dodelete = ExecBRDeleteTriggers(estate, epqstate, resultRelInfo, - tupleid); + rowid); if (!dodelete) /* "do nothing" */ return NULL; @@ -335,8 +346,16 @@ ExecDelete(ItemPointer tupleid, if (!dodelete) /* "do nothing" */ return NULL; } + else if (resultRelInfo->ri_fdwroutine) + { + FdwRoutine *fdwroutine = resultRelInfo->ri_fdwroutine; + + num_rows = fdwroutine->ExecForeignDelete(resultRelInfo, rowid); + } else { + ItemPointer tupleid = (ItemPointer) DatumGetPointer(rowid); + /* * delete the tuple * @@ -401,10 +420,10 @@ ldelete:; } if (canSetTag) - (estate->es_processed)++; + (estate->es_processed) += num_rows; /* AFTER ROW DELETE Triggers */ - ExecARDeleteTriggers(estate, resultRelInfo, tupleid); + ExecARDeleteTriggers(estate, resultRelInfo, rowid); /* Process RETURNING if present */ if (resultRelInfo->ri_projectReturning) @@ -428,7 +447,8 @@ ldelete:; } else { - deltuple.t_self = *tupleid; + ItemPointerCopy((ItemPointer)DatumGetPointer(rowid), + &deltuple.t_self); if (!heap_fetch(resultRelationDesc, SnapshotAny, &deltuple, &delbuffer, false, NULL)) elog(ERROR, "failed to fetch deleted tuple for DELETE RETURNING"); @@ -470,7 +490,7 @@ ldelete:; * ---------------------------------------------------------------- */ static TupleTableSlot * -ExecUpdate(ItemPointer tupleid, +ExecUpdate(Datum rowid, HeapTupleHeader oldtuple, TupleTableSlot *slot, TupleTableSlot *planSlot, @@ -485,6 +505,7 @@ ExecUpdate(ItemPointer tupleid, ItemPointerData update_ctid; TransactionId update_xmax; List *recheckIndexes = NIL; + int num_rows = 1; /* * abort the operation if not running transactions @@ -509,7 +530,7 @@ ExecUpdate(ItemPointer tupleid, resultRelInfo->ri_TrigDesc->trig_update_before_row) { slot = ExecBRUpdateTriggers(estate, epqstate, resultRelInfo, - tupleid, slot); + rowid, slot); if (slot == NULL) /* "do nothing" */ return NULL; @@ -539,8 +560,16 @@ ExecUpdate(ItemPointer tupleid, /* trigger might have changed tuple */ tuple = ExecMaterializeSlot(slot); } + else if (resultRelInfo->ri_fdwroutine) + { + FdwRoutine *fdwroutine = resultRelInfo->ri_fdwroutine; + + num_rows = fdwroutine->ExecForeignUpdate(resultRelInfo, rowid, tuple); + } else { + ItemPointer tupleid = (ItemPointer) DatumGetPointer(rowid); + /* * Check the constraints of the tuple * @@ -630,10 +659,10 @@ lreplace:; } if (canSetTag) - (estate->es_processed)++; + (estate->es_processed) += num_rows; /* AFTER ROW UPDATE Triggers */ - ExecARUpdateTriggers(estate, resultRelInfo, tupleid, tuple, + ExecARUpdateTriggers(estate, resultRelInfo, rowid, tuple, recheckIndexes); list_free(recheckIndexes); @@ -714,6 +743,7 @@ ExecModifyTable(ModifyTableState *node) TupleTableSlot *planSlot; ItemPointer tupleid = NULL; ItemPointerData tuple_ctid; + Datum rowid = 0; HeapTupleHeader oldtuple = NULL; /* @@ -802,17 +832,19 @@ ExecModifyTable(ModifyTableState *node) if (junkfilter != NULL) { /* - * extract the 'ctid' or 'wholerow' junk attribute. + * extract the 'ctid', 'rowid' or 'wholerow' junk attribute. */ if (operation == CMD_UPDATE || operation == CMD_DELETE) { + char relkind; Datum datum; bool isNull; - if (resultRelInfo->ri_RelationDesc->rd_rel->relkind == RELKIND_RELATION) + relkind = resultRelInfo->ri_RelationDesc->rd_rel->relkind; + if (relkind == RELKIND_RELATION) { datum = ExecGetJunkAttribute(slot, - junkfilter->jf_junkAttNo, + junkfilter->jf_junkRowidNo, &isNull); /* shouldn't ever get a null result... */ if (isNull) @@ -820,13 +852,33 @@ ExecModifyTable(ModifyTableState *node) tupleid = (ItemPointer) DatumGetPointer(datum); tuple_ctid = *tupleid; /* be sure we don't free - * ctid!! */ - tupleid = &tuple_ctid; + * ctid ! */ + rowid = PointerGetDatum(&tuple_ctid); + } + else if (relkind == RELKIND_FOREIGN_TABLE) + { + datum = ExecGetJunkAttribute(slot, + junkfilter->jf_junkRowidNo, + &isNull); + /* shouldn't ever get a null result... */ + if (isNull) + elog(ERROR, "rowid is NULL"); + + rowid = datum; + + datum = ExecGetJunkAttribute(slot, + junkfilter->jf_junkRecordNo, + &isNull); + /* shouldn't ever get a null result... */ + if (isNull) + elog(ERROR, "wholerow is NULL"); + + oldtuple = DatumGetHeapTupleHeader(datum); } else { datum = ExecGetJunkAttribute(slot, - junkfilter->jf_junkAttNo, + junkfilter->jf_junkRecordNo, &isNull); /* shouldn't ever get a null result... */ if (isNull) @@ -849,11 +901,11 @@ ExecModifyTable(ModifyTableState *node) slot = ExecInsert(slot, planSlot, estate, node->canSetTag); break; case CMD_UPDATE: - slot = ExecUpdate(tupleid, oldtuple, slot, planSlot, + slot = ExecUpdate(rowid, oldtuple, slot, planSlot, &node->mt_epqstate, estate, node->canSetTag); break; case CMD_DELETE: - slot = ExecDelete(tupleid, oldtuple, planSlot, + slot = ExecDelete(rowid, oldtuple, planSlot, &node->mt_epqstate, estate, node->canSetTag); break; default: @@ -965,6 +1017,22 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) estate->es_result_relation_info = resultRelInfo; mtstate->mt_plans[i] = ExecInitNode(subplan, estate, eflags); + /* + * Also tells FDW extensions to init the plan for this result rel + */ + if (RelationGetForm(resultRelInfo->ri_RelationDesc)->relkind == RELKIND_FOREIGN_TABLE) + { + Oid relid = RelationGetRelid(resultRelInfo->ri_RelationDesc); + FdwRoutine *fdwroutine = GetFdwRoutineByRelId(relid); + + Assert(fdwroutine != NULL); + resultRelInfo->ri_fdwroutine = fdwroutine; + resultRelInfo->ri_fdw_state = NULL; + + if (fdwroutine->BeginForeignModify) + fdwroutine->BeginForeignModify(operation, resultRelInfo, + estate, subplan, eflags); + } resultRelInfo++; i++; } @@ -1106,6 +1174,8 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) for (i = 0; i < nplans; i++) { JunkFilter *j; + char relkind = + RelationGetForm(resultRelInfo->ri_RelationDesc)->relkind; subplan = mtstate->mt_plans[i]->plan; if (operation == CMD_INSERT || operation == CMD_UPDATE) @@ -1119,16 +1189,27 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) if (operation == CMD_UPDATE || operation == CMD_DELETE) { /* For UPDATE/DELETE, find the appropriate junk attr now */ - if (resultRelInfo->ri_RelationDesc->rd_rel->relkind == RELKIND_RELATION) + if (relkind == RELKIND_RELATION) { - j->jf_junkAttNo = ExecFindJunkAttribute(j, "ctid"); - if (!AttributeNumberIsValid(j->jf_junkAttNo)) + j->jf_junkRowidNo = ExecFindJunkAttribute(j, "ctid"); + if (!AttributeNumberIsValid(j->jf_junkRowidNo)) elog(ERROR, "could not find junk ctid column"); } + else if (relkind == RELKIND_FOREIGN_TABLE) + { + j->jf_junkRowidNo = ExecFindJunkAttribute(j, "rowid"); + if (!AttributeNumberIsValid(j->jf_junkRowidNo)) + elog(ERROR, "could not find junk rowid column"); + j->jf_junkRecordNo + = ExecFindJunkAttribute(j, "record"); + if (!AttributeNumberIsValid(j->jf_junkRecordNo)) + elog(ERROR, "could not find junk wholerow column"); + } else { - j->jf_junkAttNo = ExecFindJunkAttribute(j, "wholerow"); - if (!AttributeNumberIsValid(j->jf_junkAttNo)) + j->jf_junkRecordNo + = ExecFindJunkAttribute(j, "wholerow"); + if (!AttributeNumberIsValid(j->jf_junkRecordNo)) elog(ERROR, "could not find junk wholerow column"); } } @@ -1182,6 +1263,21 @@ ExecEndModifyTable(ModifyTableState *node) { int i; + /* Let the FDW shut dowm */ + for (i=0; i < node->ps.state->es_num_result_relations; i++) + { + ResultRelInfo *resultRelInfo + = &node->ps.state->es_result_relations[i]; + + if (resultRelInfo->ri_fdwroutine) + { + FdwRoutine *fdwroutine = resultRelInfo->ri_fdwroutine; + + if (fdwroutine->EndForeignModify) + fdwroutine->EndForeignModify(resultRelInfo); + } + } + /* * Free the exprcontext */ diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index f34f704..3983e87 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -594,6 +594,7 @@ _copyForeignScan(const ForeignScan *from) COPY_NODE_FIELD(fdw_exprs); COPY_NODE_FIELD(fdw_private); COPY_SCALAR_FIELD(fsSystemCol); + COPY_SCALAR_FIELD(fsPseudoCol); return newnode; } diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index 02a0f62..6f16ef0 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -562,6 +562,7 @@ _outForeignScan(StringInfo str, const ForeignScan *node) WRITE_NODE_FIELD(fdw_exprs); WRITE_NODE_FIELD(fdw_private); WRITE_BOOL_FIELD(fsSystemCol); + WRITE_BOOL_FIELD(fsPseudoCol); } static void diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c index 030f420..ff3b9ff 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -39,6 +39,7 @@ #include "parser/parse_clause.h" #include "parser/parsetree.h" #include "utils/lsyscache.h" +#include "utils/rel.h" static Plan *create_plan_recurse(PlannerInfo *root, Path *best_path); @@ -1943,6 +1944,8 @@ create_foreignscan_plan(PlannerInfo *root, ForeignPath *best_path, RelOptInfo *rel = best_path->path.parent; Index scan_relid = rel->relid; RangeTblEntry *rte; + Relation relation; + AttrNumber num_attrs; int i; /* it should be a base rel... */ @@ -2001,6 +2004,22 @@ create_foreignscan_plan(PlannerInfo *root, ForeignPath *best_path, } } + /* + * Also, detect whether any pseudo columns are requested from rel. + */ + relation = heap_open(rte->relid, NoLock); + scan_plan->fsPseudoCol = false; + num_attrs = RelationGetNumberOfAttributes(relation); + for (i = num_attrs + 1; i <= rel->max_attr; i++) + { + if (!bms_is_empty(rel->attr_needed[i - rel->min_attr])) + { + scan_plan->fsPseudoCol = true; + break; + } + } + heap_close(relation, NoLock); + return scan_plan; } diff --git a/src/backend/optimizer/plan/initsplan.c b/src/backend/optimizer/plan/initsplan.c index 9565e2d..740bb1e 100644 --- a/src/backend/optimizer/plan/initsplan.c +++ b/src/backend/optimizer/plan/initsplan.c @@ -82,7 +82,7 @@ static void check_hashjoinable(RestrictInfo *restrictinfo); * "other rel" RelOptInfos for the members of any appendrels we find here.) */ void -add_base_rels_to_query(PlannerInfo *root, Node *jtnode) +add_base_rels_to_query(PlannerInfo *root, List *tlist, Node *jtnode) { if (jtnode == NULL) return; @@ -90,7 +90,7 @@ add_base_rels_to_query(PlannerInfo *root, Node *jtnode) { int varno = ((RangeTblRef *) jtnode)->rtindex; - (void) build_simple_rel(root, varno, RELOPT_BASEREL); + (void) build_simple_rel(root, varno, tlist, RELOPT_BASEREL); } else if (IsA(jtnode, FromExpr)) { @@ -98,14 +98,14 @@ add_base_rels_to_query(PlannerInfo *root, Node *jtnode) ListCell *l; foreach(l, f->fromlist) - add_base_rels_to_query(root, lfirst(l)); + add_base_rels_to_query(root, tlist, lfirst(l)); } else if (IsA(jtnode, JoinExpr)) { JoinExpr *j = (JoinExpr *) jtnode; - add_base_rels_to_query(root, j->larg); - add_base_rels_to_query(root, j->rarg); + add_base_rels_to_query(root, tlist, j->larg); + add_base_rels_to_query(root, tlist, j->rarg); } else elog(ERROR, "unrecognized node type: %d", diff --git a/src/backend/optimizer/plan/planmain.c b/src/backend/optimizer/plan/planmain.c index c2488a4..2d292dd 100644 --- a/src/backend/optimizer/plan/planmain.c +++ b/src/backend/optimizer/plan/planmain.c @@ -163,7 +163,7 @@ query_planner(PlannerInfo *root, List *tlist, * rangetable may contain RTEs for rels not actively part of the query, * for example views. We don't want to make RelOptInfos for them. */ - add_base_rels_to_query(root, (Node *) parse->jointree); + add_base_rels_to_query(root, tlist, (Node *) parse->jointree); /* * Examine the targetlist and join tree, adding entries to baserel diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index 385e646..7951762 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -3310,7 +3310,7 @@ plan_cluster_use_sort(Oid tableOid, Oid indexOid) setup_simple_rel_arrays(root); /* Build RelOptInfo */ - rel = build_simple_rel(root, 1, RELOPT_BASEREL); + rel = build_simple_rel(root, 1, NIL, RELOPT_BASEREL); /* Locate IndexOptInfo for the target index */ indexInfo = NULL; diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c index 9e154e1..391721e 100644 --- a/src/backend/optimizer/prep/prepunion.c +++ b/src/backend/optimizer/prep/prepunion.c @@ -236,7 +236,8 @@ recurse_set_operations(Node *setOp, PlannerInfo *root, * used for anything here, but it carries the subroot data structures * forward to setrefs.c processing. */ - rel = build_simple_rel(root, rtr->rtindex, RELOPT_BASEREL); + rel = build_simple_rel(root, rtr->rtindex, refnames_tlist, + RELOPT_BASEREL); /* plan_params should not be in use in current query level */ Assert(root->plan_params == NIL); diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c index 25f8785..3b25ab5 100644 --- a/src/backend/optimizer/util/plancat.c +++ b/src/backend/optimizer/util/plancat.c @@ -25,6 +25,7 @@ #include "access/xlog.h" #include "catalog/catalog.h" #include "catalog/heap.h" +#include "foreign/fdwapi.h" #include "miscadmin.h" #include "nodes/makefuncs.h" #include "optimizer/clauses.h" @@ -80,7 +81,7 @@ static List *build_index_tlist(PlannerInfo *root, IndexOptInfo *index, */ void get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent, - RelOptInfo *rel) + List *tlist, RelOptInfo *rel) { Index varno = rel->relid; Relation relation; @@ -104,6 +105,26 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent, rel->max_attr = RelationGetNumberOfAttributes(relation); rel->reltablespace = RelationGetForm(relation)->reltablespace; + /* + * Adjust width of attr_needed slot in case when FDW extension wants + * or needs to return pseudo-columns also, not only columns in its + * table definition. + * GetForeignRelInfo, an optional FDW handler, enables FDW extension + * to save properties of pseudo-column on its private field. + * When foreign-table is the target of UPDATE/DELETE, query-rewritter + * injects "rowid" pseudo-column to track remote row to be modified, + * so FDW has to track which varattno shall perform as "rowid". + */ + if (RelationGetForm(relation)->relkind == RELKIND_FOREIGN_TABLE) + { + FdwRoutine *fdwroutine = GetFdwRoutineByRelId(relationObjectId); + + if (fdwroutine->GetForeignRelInfo) + fdwroutine->GetForeignRelInfo(root, rel, + relationObjectId, + inhparent, tlist); + } + Assert(rel->max_attr >= rel->min_attr); rel->attr_needed = (Relids *) palloc0((rel->max_attr - rel->min_attr + 1) * sizeof(Relids)); diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c index f724714..84b674c 100644 --- a/src/backend/optimizer/util/relnode.c +++ b/src/backend/optimizer/util/relnode.c @@ -80,7 +80,8 @@ setup_simple_rel_arrays(PlannerInfo *root) * Construct a new RelOptInfo for a base relation or 'other' relation. */ RelOptInfo * -build_simple_rel(PlannerInfo *root, int relid, RelOptKind reloptkind) +build_simple_rel(PlannerInfo *root, int relid, List *tlist, + RelOptKind reloptkind) { RelOptInfo *rel; RangeTblEntry *rte; @@ -133,7 +134,7 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptKind reloptkind) { case RTE_RELATION: /* Table --- retrieve statistics from the system catalogs */ - get_relation_info(root, rte->relid, rte->inh, rel); + get_relation_info(root, rte->relid, rte->inh, tlist, rel); break; case RTE_SUBQUERY: case RTE_FUNCTION: @@ -180,7 +181,7 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptKind reloptkind) if (appinfo->parent_relid != relid) continue; - (void) build_simple_rel(root, appinfo->child_relid, + (void) build_simple_rel(root, appinfo->child_relid, tlist, RELOPT_OTHER_MEMBER_REL); } } diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c index 8f75948..e78992b 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -1163,7 +1163,10 @@ rewriteTargetListUD(Query *parsetree, RangeTblEntry *target_rte, Relation target_relation) { Var *var; - const char *attrname; + List *varList; + List *attNameList; + ListCell *cell1; + ListCell *cell2; TargetEntry *tle; if (target_relation->rd_rel->relkind == RELKIND_RELATION) @@ -1177,8 +1180,35 @@ rewriteTargetListUD(Query *parsetree, RangeTblEntry *target_rte, -1, InvalidOid, 0); + varList = list_make1(var); + attNameList = list_make1("ctid"); + } + else if (target_relation->rd_rel->relkind == RELKIND_FOREIGN_TABLE) + { + /* + * Emit Rowid so that executor can find the row to update or delete. + */ + var = makeVar(parsetree->resultRelation, + RelationGetNumberOfAttributes(target_relation) + 1, + INTERNALOID, + -1, + InvalidOid, + 0); + varList = list_make1(var); - attrname = "ctid"; + /* + * Emit generic record Var so that executor will have the "old" view + * row to pass the RETURNING clause (or upcoming triggers). + */ + var = makeVar(parsetree->resultRelation, + InvalidAttrNumber, + RECORDOID, + -1, + InvalidOid, + 0); + varList = lappend(varList, var); + + attNameList = list_make2("rowid", "record"); } else { @@ -1190,16 +1220,21 @@ rewriteTargetListUD(Query *parsetree, RangeTblEntry *target_rte, parsetree->resultRelation, 0, false); - - attrname = "wholerow"; + varList = list_make1(var); + attNameList = list_make1("wholerow"); } - tle = makeTargetEntry((Expr *) var, - list_length(parsetree->targetList) + 1, - pstrdup(attrname), - true); - - parsetree->targetList = lappend(parsetree->targetList, tle); + /* + * Append them to targetList + */ + forboth (cell1, varList, cell2, attNameList) + { + tle = makeTargetEntry((Expr *)lfirst(cell1), + list_length(parsetree->targetList) + 1, + pstrdup((const char *)lfirst(cell2)), + true); + parsetree->targetList = lappend(parsetree->targetList, tle); + } } diff --git a/src/include/commands/trigger.h b/src/include/commands/trigger.h index e790b9d..8370e05 100644 --- a/src/include/commands/trigger.h +++ b/src/include/commands/trigger.h @@ -147,10 +147,10 @@ extern void ExecASDeleteTriggers(EState *estate, extern bool ExecBRDeleteTriggers(EState *estate, EPQState *epqstate, ResultRelInfo *relinfo, - ItemPointer tupleid); + Datum rowid); extern void ExecARDeleteTriggers(EState *estate, ResultRelInfo *relinfo, - ItemPointer tupleid); + Datum rowid); extern bool ExecIRDeleteTriggers(EState *estate, ResultRelInfo *relinfo, HeapTuple trigtuple); @@ -161,11 +161,11 @@ extern void ExecASUpdateTriggers(EState *estate, extern TupleTableSlot *ExecBRUpdateTriggers(EState *estate, EPQState *epqstate, ResultRelInfo *relinfo, - ItemPointer tupleid, + Datum rowid, TupleTableSlot *slot); extern void ExecARUpdateTriggers(EState *estate, ResultRelInfo *relinfo, - ItemPointer tupleid, + Datum rowid, HeapTuple newtuple, List *recheckIndexes); extern TupleTableSlot *ExecIRUpdateTriggers(EState *estate, diff --git a/src/include/foreign/fdwapi.h b/src/include/foreign/fdwapi.h index 721cd25..8b29532 100644 --- a/src/include/foreign/fdwapi.h +++ b/src/include/foreign/fdwapi.h @@ -22,6 +22,11 @@ struct ExplainState; /* * Callback function signatures --- see fdwhandler.sgml for more info. */ +typedef void (*GetForeignRelInfo_function) (PlannerInfo *root, + RelOptInfo *baserel, + Oid foreigntableid, + bool inhparent, + List *targetList); typedef void (*GetForeignRelSize_function) (PlannerInfo *root, RelOptInfo *baserel, @@ -59,6 +64,20 @@ typedef bool (*AnalyzeForeignTable_function) (Relation relation, AcquireSampleRowsFunc *func, BlockNumber *totalpages); +typedef void (*BeginForeignModify_function) (CmdType operation, + ResultRelInfo *resultRelInfo, + EState *estate, + Plan *subplan, + int eflags); +typedef int (*ExecForeignInsert_function) (ResultRelInfo *resultRelInfo, + HeapTuple tuple); +typedef int (*ExecForeignDelete_function) (ResultRelInfo *resultRelInfo, + Datum rowid); +typedef int (*ExecForeignUpdate_function) (ResultRelInfo *resultRelInfo, + Datum rowid, + HeapTuple tuple); +typedef void (*EndForeignModify_function) (ResultRelInfo *resultRelInfo); + /* * FdwRoutine is the struct returned by a foreign-data wrapper's handler * function. It provides pointers to the callback functions needed by the @@ -90,6 +109,12 @@ typedef struct FdwRoutine * not provided. */ AnalyzeForeignTable_function AnalyzeForeignTable; + GetForeignRelInfo_function GetForeignRelInfo; + BeginForeignModify_function BeginForeignModify; + ExecForeignInsert_function ExecForeignInsert; + ExecForeignDelete_function ExecForeignDelete; + ExecForeignUpdate_function ExecForeignUpdate; + EndForeignModify_function EndForeignModify; } FdwRoutine; diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index fec07b8..d8fa299 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -268,9 +268,11 @@ typedef struct ProjectionInfo * attribute numbers of the "original" tuple and the * attribute numbers of the "clean" tuple. * resultSlot: tuple slot used to hold cleaned tuple. - * junkAttNo: not used by junkfilter code. Can be used by caller - * to remember the attno of a specific junk attribute + * jf_junkRowidNo: not used by junkfilter code. Can be used by caller + * to remember the attno of to track a particular tuple + * being updated or deleted. * (execMain.c stores the "ctid" attno here). + * jf_junkRecordNo: Also, the attno of whole-row reference. * ---------------- */ typedef struct JunkFilter @@ -280,7 +282,8 @@ typedef struct JunkFilter TupleDesc jf_cleanTupType; AttrNumber *jf_cleanMap; TupleTableSlot *jf_resultSlot; - AttrNumber jf_junkAttNo; + AttrNumber jf_junkRowidNo; + AttrNumber jf_junkRecordNo; } JunkFilter; /* ---------------- @@ -303,6 +306,8 @@ typedef struct JunkFilter * ConstraintExprs array of constraint-checking expr states * junkFilter for removing junk attributes from tuples * projectReturning for computing a RETURNING list + * fdwroutine FDW callbacks if foreign table + * fdw_state opaque state of FDW module, or NULL * ---------------- */ typedef struct ResultRelInfo @@ -320,6 +325,8 @@ typedef struct ResultRelInfo List **ri_ConstraintExprs; JunkFilter *ri_junkFilter; ProjectionInfo *ri_projectReturning; + struct FdwRoutine *ri_fdwroutine; + void *ri_fdw_state; } ResultRelInfo; /* ---------------- diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h index fb9a863..a724863 100644 --- a/src/include/nodes/plannodes.h +++ b/src/include/nodes/plannodes.h @@ -478,6 +478,7 @@ typedef struct ForeignScan List *fdw_exprs; /* expressions that FDW may evaluate */ List *fdw_private; /* private data for FDW */ bool fsSystemCol; /* true if any "system column" is needed */ + bool fsPseudoCol; /* true if any "pseudo column" is needed */ } ForeignScan; diff --git a/src/include/optimizer/pathnode.h b/src/include/optimizer/pathnode.h index de889fb..adfc93d 100644 --- a/src/include/optimizer/pathnode.h +++ b/src/include/optimizer/pathnode.h @@ -133,7 +133,7 @@ extern Path *reparameterize_path(PlannerInfo *root, Path *path, */ extern void setup_simple_rel_arrays(PlannerInfo *root); extern RelOptInfo *build_simple_rel(PlannerInfo *root, int relid, - RelOptKind reloptkind); + List *tlist, RelOptKind reloptkind); extern RelOptInfo *find_base_rel(PlannerInfo *root, int relid); extern RelOptInfo *find_join_rel(PlannerInfo *root, Relids relids); extern RelOptInfo *build_join_rel(PlannerInfo *root, diff --git a/src/include/optimizer/plancat.h b/src/include/optimizer/plancat.h index e0d04db..c5fce41 100644 --- a/src/include/optimizer/plancat.h +++ b/src/include/optimizer/plancat.h @@ -26,7 +26,7 @@ extern PGDLLIMPORT get_relation_info_hook_type get_relation_info_hook; extern void get_relation_info(PlannerInfo *root, Oid relationObjectId, - bool inhparent, RelOptInfo *rel); + bool inhparent, List *tlist, RelOptInfo *rel); extern void estimate_rel_size(Relation rel, int32 *attr_widths, BlockNumber *pages, double *tuples, double *allvisfrac); diff --git a/src/include/optimizer/planmain.h b/src/include/optimizer/planmain.h index c395d42..d8bc7da 100644 --- a/src/include/optimizer/planmain.h +++ b/src/include/optimizer/planmain.h @@ -90,7 +90,8 @@ extern bool is_projection_capable_plan(Plan *plan); extern int from_collapse_limit; extern int join_collapse_limit; -extern void add_base_rels_to_query(PlannerInfo *root, Node *jtnode); +extern void add_base_rels_to_query(PlannerInfo *root, List *tlist, + Node *jtnode); extern void build_base_rel_tlists(PlannerInfo *root, List *final_tlist); extern void add_vars_to_targetlist(PlannerInfo *root, List *vars, Relids where_needed, bool create_new_ph);