diff --git a/doc/src/sgml/ddl.sgml b/doc/src/sgml/ddl.sgml index d6e5d64..30f9c6b 100644 --- a/doc/src/sgml/ddl.sgml +++ b/doc/src/sgml/ddl.sgml @@ -3066,11 +3066,6 @@ ANALYZE measurement; user mapping, which can provide additional options based on the current PostgreSQL role. - - - Currently, foreign tables are read-only. This limitation may be fixed - in a future release. - diff --git a/doc/src/sgml/fdwhandler.sgml b/doc/src/sgml/fdwhandler.sgml index 2d604ed..c4c05e6 100644 --- a/doc/src/sgml/fdwhandler.sgml +++ b/doc/src/sgml/fdwhandler.sgml @@ -89,6 +89,54 @@ +AttrNumber +GetForeignRelWidth(PlannerInfo *root, + RelOptInfo *baserel, + Relation foreignrel, + bool inhparent, + List *targetList); + + Obtain the width of the result set to be fetched during a foreign table scan. + This is an optional handler, and called before GetForeignRelSize + for a query involving a foreign table + (during the construction of RelOptInfo). + root is the planner's global information about the query, + baserel is the planner's information being constructed for + this query, and foreignrel is a Relation + descriptor of the foreign table. + inhparent is a boolean to show whether the relation is + an inheritance parent, even though foreign tables do not support table + inheritance right now. targetList is the list of + TargetEntry to be returned from the (sub-)query + that is currently in focus. + + + + The result value of this function will be assigned to + baserel->max_attr, that means it is the expected number + of columns being fetched during the foreign table scan. + It should not be smaller than the number of regular columns in the definition + of this foreign table. You can only return a number greater than his value to + acquire slots for some additional attributes, which are called + pseudo-columns. + A typical usage of a pseudo-column is to carry an identifier of + a particular remote row to be updated or deleted from the scanning stage + to the modifying stage when the foreign table is the target of + a data-modifying SQL statement. + You can return the result of the helper function + get_pseudo_rowid_column if this "rowid" + pseudo-column is the only one you need. + + + + In addition to that, pseudo-columns can be used to off-load the burden of + complex calculations to foreign computing resources by replacing an + expression with a reference to its result, which is calculated on the + remote side rather than locally. + + + + void GetForeignRelSize (PlannerInfo *root, RelOptInfo *baserel, @@ -96,7 +144,8 @@ GetForeignRelSize (PlannerInfo *root, Obtain relation size estimates for a foreign table. This is called - at the beginning of planning for a query involving a foreign table. + for a query involving a foreign table at the beginning of planning + or right after GetForeignRelWidth, if that callback is configured. root is the planner's global information about the query; baserel is the planner's information about this table; and foreigntableid is the pg_class OID of the @@ -315,6 +364,114 @@ AcquireSampleRowsFunc (Relation relation, int elevel, + If a FDW supports writable foreign tables, it should implement + some or all of the following callback functions depending on + the needs and capabilities of the FDW. + + + + +List * +PlanForeignModify(PlannerInfo *root, + ModifyTable *plan, + Index resultRelation, + Plan *subplan); + + It allows FDW drivers to construct private information relevant to + the modification of the foreign table. This private information must have + the form of a List *, which will be delivered as + third argument to BeginForeignModify during the execution stage. + + + + root is the planner's global information about the query. + plan is the master plan to modify the result relation according + to its command type. Please consider that ModifyTable + may have multiple result relations in a future revision, even though + currently there is no support for table inheritance on foreign tables. + resultRelation is an index for the result relation in the + range table entries, and subplan is the relevant scan plan; + that should be a ForeignScan for UPDATE or + DELETE, so the driver can access the private information of + the scan stage using this argument. + + + + +void +BeginForeignModify (ModifyTableState *mtstate, + ResultRelInfo *resultRelInfo, + List *fdw_private, + Plan *subplan, + int eflags); + + It is invoked at beginning of foreign table modification, during + executor startup. This routine should perform any initialization + needed prior to the actual table modifications, but not start + modifying the actual tuples. (That should be done during each call of + ExecForeignInsert, ExecForeignUpdate or + ExecForeignDelete.) + + + + The ri_fdw_state field of ResultRelInfo + is reserved to store any private information relevant to this foreign table, + so it should be initialized to contain the per-scan state. + Please consider that ModifyTableState may have multiple + result relations in a future revision, even though currently there is no + support for table inheritance on foreign tables. resultRelInfo + is the master information connected to this foreign table. + fdw_private is private information constructed in + PlanForeignModify, and subplan is the relevant + scan plan of this table modification. + + + + +int +ExecForeignInsert (ResultRelInfo *resultRelInfo, + HeapTuple tuple); + + Insert the given tuple into backing storage on behalf of the foreign table. + The supplied tuple is already formed according to the definition of + the relation, thus all the pseudo-columns are already filtered out. + It can return the number of rows being inserted, usually 1. + + + + +int +ExecForeignDelete (ResultRelInfo *resultRelInfo, + const char *rowid); + + Delete the tuple being identified with rowid from the backing + storage on behalf of the foreign table. + It can return the number of rows being deleted, usually 1. + + + + +int +ExecForeignUpdate (ResultRelInfo *resultRelInfo, + const char *rowid, + HeapTuple tuple); + + Update the tuple being identified with rowid in the backing + storage on behalf of the foreign table with the given tuple. + It can return the number of rows being updated, usually 1. + + + + +void +EndForeignModify (ResultRelInfo *resultRelInfo); + + End the modification 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. + + + The FdwRoutine struct type is declared in src/include/foreign/fdwapi.h, which see for additional details. diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index 0222d40..60f4c94 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" @@ -933,6 +934,7 @@ void CheckValidResultRel(Relation resultRel, CmdType operation) { TriggerDesc *trigDesc = resultRel->trigdesc; + FdwRoutine *fdwroutine; switch (resultRel->rd_rel->relkind) { @@ -991,10 +993,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 d31015c..6c2b6e5 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, @@ -296,6 +306,7 @@ ExecDelete(ItemPointer tupleid, Relation resultRelationDesc; HTSU_Result result; HeapUpdateFailureData hufd; + int num_rows = 1; /* * get information on the (current) result relation @@ -310,7 +321,7 @@ ExecDelete(ItemPointer tupleid, bool dodelete; dodelete = ExecBRDeleteTriggers(estate, epqstate, resultRelInfo, - tupleid); + (ItemPointer)DatumGetPointer(rowid)); if (!dodelete) /* "do nothing" */ return NULL; @@ -334,8 +345,17 @@ ExecDelete(ItemPointer tupleid, if (!dodelete) /* "do nothing" */ return NULL; } + else if (resultRelInfo->ri_fdwroutine) + { + FdwRoutine *fdwroutine = resultRelInfo->ri_fdwroutine; + + num_rows = fdwroutine->ExecForeignDelete(resultRelInfo, + DatumGetCString(rowid)); + } else { + ItemPointer tupleid = (ItemPointer) DatumGetPointer(rowid); + /* * delete the tuple * @@ -430,10 +450,11 @@ ldelete:; } if (canSetTag) - (estate->es_processed)++; + (estate->es_processed) += num_rows; /* AFTER ROW DELETE Triggers */ - ExecARDeleteTriggers(estate, resultRelInfo, tupleid); + ExecARDeleteTriggers(estate, resultRelInfo, + (ItemPointer)DatumGetPointer(rowid)); /* Process RETURNING if present */ if (resultRelInfo->ri_projectReturning) @@ -457,7 +478,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"); @@ -499,7 +521,7 @@ ldelete:; * ---------------------------------------------------------------- */ static TupleTableSlot * -ExecUpdate(ItemPointer tupleid, +ExecUpdate(Datum rowid, HeapTupleHeader oldtuple, TupleTableSlot *slot, TupleTableSlot *planSlot, @@ -513,6 +535,7 @@ ExecUpdate(ItemPointer tupleid, HTSU_Result result; HeapUpdateFailureData hufd; List *recheckIndexes = NIL; + int num_rows = 1; /* * abort the operation if not running transactions @@ -537,7 +560,7 @@ ExecUpdate(ItemPointer tupleid, resultRelInfo->ri_TrigDesc->trig_update_before_row) { slot = ExecBRUpdateTriggers(estate, epqstate, resultRelInfo, - tupleid, slot); + (ItemPointer)DatumGetPointer(rowid), slot); if (slot == NULL) /* "do nothing" */ return NULL; @@ -567,8 +590,18 @@ 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, + DatumGetCString(rowid), + tuple); + } else { + ItemPointer tupleid = (ItemPointer) DatumGetPointer(rowid); + /* * Check the constraints of the tuple * @@ -687,11 +720,12 @@ lreplace:; } if (canSetTag) - (estate->es_processed)++; + (estate->es_processed) += num_rows; /* AFTER ROW UPDATE Triggers */ - ExecARUpdateTriggers(estate, resultRelInfo, tupleid, tuple, - recheckIndexes); + ExecARUpdateTriggers(estate, resultRelInfo, + (ItemPointer) DatumGetPointer(rowid), + tuple, recheckIndexes); list_free(recheckIndexes); @@ -771,6 +805,7 @@ ExecModifyTable(ModifyTableState *node) TupleTableSlot *planSlot; ItemPointer tupleid = NULL; ItemPointerData tuple_ctid; + Datum rowid = 0; HeapTupleHeader oldtuple = NULL; /* @@ -859,17 +894,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) @@ -877,13 +914,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) @@ -906,11 +963,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: @@ -997,6 +1054,8 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) i = 0; foreach(l, node->plans) { + char relkind; + subplan = (Plan *) lfirst(l); /* @@ -1022,6 +1081,24 @@ 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 + */ + relkind = RelationGetForm(resultRelInfo->ri_RelationDesc)->relkind; + if (relkind == RELKIND_FOREIGN_TABLE) + { + Oid relid = RelationGetRelid(resultRelInfo->ri_RelationDesc); + FdwRoutine *fdwroutine = GetFdwRoutineByRelId(relid); + List *fdwprivate = list_nth(node->fdwPrivList, i); + + Assert(fdwroutine != NULL); + resultRelInfo->ri_fdwroutine = fdwroutine; + resultRelInfo->ri_fdw_state = NULL; + + if (fdwroutine->BeginForeignModify) + fdwroutine->BeginForeignModify(mtstate, resultRelInfo, + fdwprivate, subplan, eflags); + } resultRelInfo++; i++; } @@ -1163,6 +1240,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) @@ -1176,16 +1255,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"); } } @@ -1239,6 +1329,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/foreign/foreign.c b/src/backend/foreign/foreign.c index d8845aa..4363a28 100644 --- a/src/backend/foreign/foreign.c +++ b/src/backend/foreign/foreign.c @@ -571,3 +571,71 @@ get_foreign_server_oid(const char *servername, bool missing_ok) errmsg("server \"%s\" does not exist", servername))); return oid; } + +/* + * get_pseudo_rowid_column + * + * It picks up an attribute number to be used for the pseudo rowid column, + * if it exists. It should be injected at rewriteHandler.c if the supplied query + * is a UPDATE or DELETE command. Elsewhere, it returns InvalidAttrNumber. + */ +AttrNumber +get_pseudo_rowid_column(RelOptInfo *baserel, List *targetList) +{ + ListCell *cell; + + foreach (cell, targetList) + { + TargetEntry *tle = lfirst(cell); + + if (tle->resjunk && + tle->resname && strcmp(tle->resname, "rowid") == 0) + { + Var *var; + + if (!IsA(tle->expr, Var)) + elog(ERROR, "unexpected node on junk rowid entry: %d", + (int) nodeTag(tle->expr)); + + var = (Var *) tle->expr; + if (baserel->relid == var->varno) + return var->varattno; + } + } + return InvalidAttrNumber; +} + +/* + * lookup_foreign_scan_plan + * + * It looks up the ForeignScan plan node with the supplied range-table id. + * Its typical usage is for PlanForeignModify to get the underlying scan plan on + * UPDATE or DELETE commands. + */ +ForeignScan * +lookup_foreign_scan_plan(Plan *subplan, Index rtindex) +{ + if (!subplan) + return NULL; + + else if (IsA(subplan, ForeignScan)) + { + ForeignScan *fscan = (ForeignScan *) subplan; + + if (fscan->scan.scanrelid == rtindex) + return fscan; + } + else + { + ForeignScan *fscan; + + fscan = lookup_foreign_scan_plan(subplan->lefttree, rtindex); + if (fscan != NULL) + return fscan; + + fscan = lookup_foreign_scan_plan(subplan->righttree, rtindex); + if (fscan != NULL) + return fscan; + } + return NULL; +} diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index 9387ee9..5b1e8ac 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -181,6 +181,7 @@ _copyModifyTable(const ModifyTable *from) COPY_NODE_FIELD(returningLists); COPY_NODE_FIELD(rowMarks); COPY_SCALAR_FIELD(epqParam); + COPY_NODE_FIELD(fdwPrivList); return newnode; } @@ -594,6 +595,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 35c6287..c32ebd3 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -335,6 +335,7 @@ _outModifyTable(StringInfo str, const ModifyTable *node) WRITE_NODE_FIELD(returningLists); WRITE_NODE_FIELD(rowMarks); WRITE_INT_FIELD(epqParam); + WRITE_NODE_FIELD(fdwPrivList); } static void @@ -562,6 +563,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..91db1b8 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; } @@ -4695,7 +4714,8 @@ make_result(PlannerInfo *root, * to make it look better sometime. */ ModifyTable * -make_modifytable(CmdType operation, bool canSetTag, +make_modifytable(PlannerInfo *root, + CmdType operation, bool canSetTag, List *resultRelations, List *subplans, List *returningLists, List *rowMarks, int epqParam) @@ -4704,6 +4724,8 @@ make_modifytable(CmdType operation, bool canSetTag, Plan *plan = &node->plan; double total_size; ListCell *subnode; + ListCell *resultRel; + List *fdw_priv_list = NIL; Assert(list_length(resultRelations) == list_length(subplans)); Assert(returningLists == NIL || @@ -4746,6 +4768,30 @@ make_modifytable(CmdType operation, bool canSetTag, node->rowMarks = rowMarks; node->epqParam = epqParam; + /* + * Allow FDW driver to construct its private plan if the result relation + * is a foreign table. + */ + forboth (resultRel, resultRelations, subnode, subplans) + { + RangeTblEntry *rte = rt_fetch(lfirst_int(resultRel), + root->parse->rtable); + List *fdw_private = NIL; + char relkind = get_rel_relkind(rte->relid); + + if (relkind == RELKIND_FOREIGN_TABLE) + { + FdwRoutine *fdwroutine = GetFdwRoutineByRelId(rte->relid); + + if (fdwroutine && fdwroutine->PlanForeignModify) + fdw_private = fdwroutine->PlanForeignModify(root, node, + lfirst_int(resultRel), + (Plan *) lfirst(subnode)); + } + fdw_priv_list = lappend(fdw_priv_list, fdw_private); + } + node->fdwPrivList = fdw_priv_list; + return node; } diff --git a/src/backend/optimizer/plan/initsplan.c b/src/backend/optimizer/plan/initsplan.c index bd719b5..57ba192 100644 --- a/src/backend/optimizer/plan/initsplan.c +++ b/src/backend/optimizer/plan/initsplan.c @@ -85,7 +85,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; @@ -93,7 +93,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)) { @@ -101,14 +101,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..4442444 100644 --- a/src/backend/optimizer/plan/planmain.c +++ b/src/backend/optimizer/plan/planmain.c @@ -112,6 +112,9 @@ query_planner(PlannerInfo *root, List *tlist, */ if (parse->jointree->fromlist == NIL) { + /* Make a flattened version of the rangetable for faster access */ + setup_simple_rel_arrays(root); + /* We need a trivial path result */ *cheapest_path = (Path *) create_result_path((List *) parse->jointree->quals); @@ -163,7 +166,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 b61005f..2d72244 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -571,7 +571,8 @@ subquery_planner(PlannerGlobal *glob, Query *parse, else rowMarks = root->rowMarks; - plan = (Plan *) make_modifytable(parse->commandType, + plan = (Plan *) make_modifytable(root, + parse->commandType, parse->canSetTag, list_make1_int(parse->resultRelation), list_make1(plan), @@ -964,7 +965,8 @@ inheritance_planner(PlannerInfo *root) rowMarks = root->rowMarks; /* And last, tack on a ModifyTable node to do the UPDATE/DELETE work */ - return (Plan *) make_modifytable(parse->commandType, + return (Plan *) make_modifytable(root, + parse->commandType, parse->canSetTag, resultRelations, subplans, @@ -3385,7 +3387,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 b91e9f4..61bc447 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 04d5028..7d6bc4b 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,30 @@ 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 FDW extension wants + * to return pseudo-columns in addition to the columns in its + * table definition. + * GetForeignRelWidth, an optional FDW handler, enables a FDW + * to save properties of a pseudo-column in its private field. + * When the foreign table is the target of UPDATE/DELETE, the query rewriter + * injects a "rowid" pseudo-column to track the remote row to be modified, + * so the FDW has to track which varattno shall perform as "rowid". + */ + if (RelationGetForm(relation)->relkind == RELKIND_FOREIGN_TABLE) + { + FdwRoutine *fdwroutine = GetFdwRoutineByRelId(relationObjectId); + + if (fdwroutine->GetForeignRelWidth) + { + rel->max_attr = fdwroutine->GetForeignRelWidth(root, rel, + relation, + inhparent, + tlist); + Assert(rel->max_attr >= RelationGetNumberOfAttributes(relation)); + } + } + 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/parser/parse_relation.c b/src/backend/parser/parse_relation.c index 36d95eb..7d4bb99 100644 --- a/src/backend/parser/parse_relation.c +++ b/src/backend/parser/parse_relation.c @@ -2087,7 +2087,20 @@ get_rte_attribute_name(RangeTblEntry *rte, AttrNumber attnum) * built (which can easily happen for rules). */ if (rte->rtekind == RTE_RELATION) - return get_relid_attribute_name(rte->relid, attnum); + { + char *attname = get_attname(rte->relid, attnum); + + if (attname) + return attname; + + /* + * XXX - If FDW driver adds pseudo-columns, it may have attribute + * number larger than number of relation's attribute. In this case, + * get_attname() returns NULL and we fall back on alias list on eref. + * It should not happen other than foreign tables. + */ + Assert(get_rel_relkind(rte->relid) == RELKIND_FOREIGN_TABLE); + } /* * Otherwise use the column name from eref. There should always be one. diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c index 990ca34..2aa46ee 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -1165,7 +1165,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) @@ -1179,8 +1182,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, + CSTRINGOID, + -2, + InvalidOid, + 0); + varList = list_make1(var); + + /* + * 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); - attrname = "ctid"; + attNameList = list_make2("rowid", "record"); } else { @@ -1192,16 +1222,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/foreign/fdwapi.h b/src/include/foreign/fdwapi.h index 721cd25..fa3ffb7 100644 --- a/src/include/foreign/fdwapi.h +++ b/src/include/foreign/fdwapi.h @@ -14,6 +14,7 @@ #include "nodes/execnodes.h" #include "nodes/relation.h" +#include "utils/rel.h" /* To avoid including explain.h here, reference ExplainState thus: */ struct ExplainState; @@ -22,6 +23,11 @@ struct ExplainState; /* * Callback function signatures --- see fdwhandler.sgml for more info. */ +typedef AttrNumber (*GetForeignRelWidth_function) (PlannerInfo *root, + RelOptInfo *baserel, + Relation foreignrel, + bool inhparent, + List *targetList); typedef void (*GetForeignRelSize_function) (PlannerInfo *root, RelOptInfo *baserel, @@ -58,6 +64,24 @@ typedef int (*AcquireSampleRowsFunc) (Relation relation, int elevel, typedef bool (*AnalyzeForeignTable_function) (Relation relation, AcquireSampleRowsFunc *func, BlockNumber *totalpages); +typedef List *(*PlanForeignModify_function) (PlannerInfo *root, + ModifyTable *plan, + Index resultRelation, + Plan *subplan); + +typedef void (*BeginForeignModify_function) (ModifyTableState *mtstate, + ResultRelInfo *resultRelInfo, + List *fdw_private, + Plan *subplan, + int eflags); +typedef int (*ExecForeignInsert_function) (ResultRelInfo *resultRelInfo, + HeapTuple tuple); +typedef int (*ExecForeignDelete_function) (ResultRelInfo *resultRelInfo, + const char *rowid); +typedef int (*ExecForeignUpdate_function) (ResultRelInfo *resultRelInfo, + const char *rowid, + HeapTuple tuple); +typedef void (*EndForeignModify_function) (ResultRelInfo *resultRelInfo); /* * FdwRoutine is the struct returned by a foreign-data wrapper's handler @@ -90,6 +114,13 @@ typedef struct FdwRoutine * not provided. */ AnalyzeForeignTable_function AnalyzeForeignTable; + GetForeignRelWidth_function GetForeignRelWidth; + PlanForeignModify_function PlanForeignModify; + BeginForeignModify_function BeginForeignModify; + ExecForeignInsert_function ExecForeignInsert; + ExecForeignDelete_function ExecForeignDelete; + ExecForeignUpdate_function ExecForeignUpdate; + EndForeignModify_function EndForeignModify; } FdwRoutine; diff --git a/src/include/foreign/foreign.h b/src/include/foreign/foreign.h index f8aa99e..b63000a 100644 --- a/src/include/foreign/foreign.h +++ b/src/include/foreign/foreign.h @@ -14,6 +14,8 @@ #define FOREIGN_H #include "nodes/parsenodes.h" +#include "nodes/plannodes.h" +#include "nodes/relation.h" /* Helper for obtaining username for user mapping */ @@ -81,4 +83,8 @@ extern List *GetForeignColumnOptions(Oid relid, AttrNumber attnum); extern Oid get_foreign_data_wrapper_oid(const char *fdwname, bool missing_ok); extern Oid get_foreign_server_oid(const char *servername, bool missing_ok); +extern AttrNumber get_pseudo_rowid_column(RelOptInfo *baserel, + List *targetList); +extern ForeignScan *lookup_foreign_scan_plan(Plan *subplan, + Index rtindex); #endif /* FOREIGN_H */ diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index d4911bd..18ab231 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 used 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..93481aa 100644 --- a/src/include/nodes/plannodes.h +++ b/src/include/nodes/plannodes.h @@ -175,6 +175,7 @@ typedef struct ModifyTable List *returningLists; /* per-target-table RETURNING tlists */ List *rowMarks; /* PlanRowMarks (non-locking only) */ int epqParam; /* ID of Param for EvalPlanQual re-eval */ + List *fdwPrivList; /* private fields for foreign tables */ } ModifyTable; /* ---------------- @@ -478,6 +479,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 0fe696c..746a603 100644 --- a/src/include/optimizer/planmain.h +++ b/src/include/optimizer/planmain.h @@ -79,7 +79,8 @@ extern SetOp *make_setop(SetOpCmd cmd, SetOpStrategy strategy, Plan *lefttree, long numGroups, double outputRows); extern Result *make_result(PlannerInfo *root, List *tlist, Node *resconstantqual, Plan *subplan); -extern ModifyTable *make_modifytable(CmdType operation, bool canSetTag, +extern ModifyTable *make_modifytable(PlannerInfo *root, + CmdType operation, bool canSetTag, List *resultRelations, List *subplans, List *returningLists, List *rowMarks, int epqParam); extern bool is_projection_capable_plan(Plan *plan); @@ -90,7 +91,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);