mirror of
https://github.com/postgres/postgres.git
synced 2025-07-31 22:04:40 +03:00
Directly modify foreign tables.
postgres_fdw can now sent an UPDATE or DELETE statement directly to the foreign server in simple cases, rather than sending a SELECT FOR UPDATE statement and then updating or deleting rows one-by-one. Etsuro Fujita, reviewed by Rushabh Lathia, Shigeru Hanada, Kyotaro Horiguchi, Albe Laurenz, Thom Brown, and me.
This commit is contained in:
@ -900,7 +900,29 @@ ExplainNode(PlanState *planstate, List *ancestors,
|
||||
pname = sname = "WorkTable Scan";
|
||||
break;
|
||||
case T_ForeignScan:
|
||||
pname = sname = "Foreign Scan";
|
||||
sname = "Foreign Scan";
|
||||
switch (((ForeignScan *) plan)->operation)
|
||||
{
|
||||
case CMD_SELECT:
|
||||
pname = "Foreign Scan";
|
||||
operation = "Select";
|
||||
break;
|
||||
case CMD_INSERT:
|
||||
pname = "Foreign Insert";
|
||||
operation = "Insert";
|
||||
break;
|
||||
case CMD_UPDATE:
|
||||
pname = "Foreign Update";
|
||||
operation = "Update";
|
||||
break;
|
||||
case CMD_DELETE:
|
||||
pname = "Foreign Delete";
|
||||
operation = "Delete";
|
||||
break;
|
||||
default:
|
||||
pname = "???";
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case T_CustomScan:
|
||||
sname = "Custom Scan";
|
||||
@ -1648,6 +1670,19 @@ show_plan_tlist(PlanState *planstate, List *ancestors, ExplainState *es)
|
||||
return;
|
||||
if (IsA(plan, RecursiveUnion))
|
||||
return;
|
||||
/*
|
||||
* Likewise for ForeignScan that executes a direct INSERT/UPDATE/DELETE
|
||||
*
|
||||
* Note: the tlist for a ForeignScan that executes a direct INSERT/UPDATE
|
||||
* might contain subplan output expressions that are confusing in this
|
||||
* context. The tlist for a ForeignScan that executes a direct UPDATE/
|
||||
* DELETE always contains "junk" target columns to identify the exact row
|
||||
* to update or delete, which would be confusing in this context. So, we
|
||||
* suppress it in all the cases.
|
||||
*/
|
||||
if (IsA(plan, ForeignScan) &&
|
||||
((ForeignScan *) plan)->operation != CMD_SELECT)
|
||||
return;
|
||||
|
||||
/* Set up deparsing context */
|
||||
context = set_deparse_context_planstate(es->deparse_cxt,
|
||||
@ -2236,8 +2271,16 @@ show_foreignscan_info(ForeignScanState *fsstate, ExplainState *es)
|
||||
FdwRoutine *fdwroutine = fsstate->fdwroutine;
|
||||
|
||||
/* Let the FDW emit whatever fields it wants */
|
||||
if (fdwroutine->ExplainForeignScan != NULL)
|
||||
fdwroutine->ExplainForeignScan(fsstate, es);
|
||||
if (((ForeignScan *) fsstate->ss.ps.plan)->operation != CMD_SELECT)
|
||||
{
|
||||
if (fdwroutine->ExplainDirectModify != NULL)
|
||||
fdwroutine->ExplainDirectModify(fsstate, es);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (fdwroutine->ExplainForeignScan != NULL)
|
||||
fdwroutine->ExplainForeignScan(fsstate, es);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@ -2623,8 +2666,10 @@ show_modifytable_info(ModifyTableState *mtstate, List *ancestors,
|
||||
}
|
||||
}
|
||||
|
||||
/* Give FDW a chance */
|
||||
if (fdwroutine && fdwroutine->ExplainForeignModify != NULL)
|
||||
/* Give FDW a chance if needed */
|
||||
if (!resultRelInfo->ri_usesFdwDirectModify &&
|
||||
fdwroutine != NULL &&
|
||||
fdwroutine->ExplainForeignModify != NULL)
|
||||
{
|
||||
List *fdw_private = (List *) list_nth(node->fdwPrivLists, j);
|
||||
|
||||
|
@ -1245,6 +1245,7 @@ InitResultRelInfo(ResultRelInfo *resultRelInfo,
|
||||
else
|
||||
resultRelInfo->ri_FdwRoutine = NULL;
|
||||
resultRelInfo->ri_FdwState = NULL;
|
||||
resultRelInfo->ri_usesFdwDirectModify = false;
|
||||
resultRelInfo->ri_ConstraintExprs = NULL;
|
||||
resultRelInfo->ri_junkFilter = NULL;
|
||||
resultRelInfo->ri_projectReturning = NULL;
|
||||
|
@ -48,7 +48,10 @@ ForeignNext(ForeignScanState *node)
|
||||
|
||||
/* Call the Iterate function in short-lived context */
|
||||
oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
|
||||
slot = node->fdwroutine->IterateForeignScan(node);
|
||||
if (plan->operation != CMD_SELECT)
|
||||
slot = node->fdwroutine->IterateDirectModify(node);
|
||||
else
|
||||
slot = node->fdwroutine->IterateForeignScan(node);
|
||||
MemoryContextSwitchTo(oldcontext);
|
||||
|
||||
/*
|
||||
@ -226,7 +229,10 @@ ExecInitForeignScan(ForeignScan *node, EState *estate, int eflags)
|
||||
/*
|
||||
* Tell the FDW to initialize the scan.
|
||||
*/
|
||||
fdwroutine->BeginForeignScan(scanstate, eflags);
|
||||
if (node->operation != CMD_SELECT)
|
||||
fdwroutine->BeginDirectModify(scanstate, eflags);
|
||||
else
|
||||
fdwroutine->BeginForeignScan(scanstate, eflags);
|
||||
|
||||
return scanstate;
|
||||
}
|
||||
@ -240,8 +246,13 @@ ExecInitForeignScan(ForeignScan *node, EState *estate, int eflags)
|
||||
void
|
||||
ExecEndForeignScan(ForeignScanState *node)
|
||||
{
|
||||
ForeignScan *plan = (ForeignScan *) node->ss.ps.plan;
|
||||
|
||||
/* Let the FDW shut down */
|
||||
node->fdwroutine->EndForeignScan(node);
|
||||
if (plan->operation != CMD_SELECT)
|
||||
node->fdwroutine->EndDirectModify(node);
|
||||
else
|
||||
node->fdwroutine->EndForeignScan(node);
|
||||
|
||||
/* Shut down any outer plan. */
|
||||
if (outerPlanState(node))
|
||||
|
@ -138,13 +138,17 @@ ExecCheckPlanOutput(Relation resultRel, List *targetList)
|
||||
* tupleSlot: slot holding tuple actually inserted/updated/deleted
|
||||
* planSlot: slot holding tuple returned by top subplan node
|
||||
*
|
||||
* Note: If tupleSlot is NULL, the FDW should have already provided econtext's
|
||||
* scan tuple.
|
||||
*
|
||||
* Returns a slot holding the result tuple
|
||||
*/
|
||||
static TupleTableSlot *
|
||||
ExecProcessReturning(ProjectionInfo *projectReturning,
|
||||
ExecProcessReturning(ResultRelInfo *resultRelInfo,
|
||||
TupleTableSlot *tupleSlot,
|
||||
TupleTableSlot *planSlot)
|
||||
{
|
||||
ProjectionInfo *projectReturning = resultRelInfo->ri_projectReturning;
|
||||
ExprContext *econtext = projectReturning->pi_exprContext;
|
||||
|
||||
/*
|
||||
@ -154,7 +158,20 @@ ExecProcessReturning(ProjectionInfo *projectReturning,
|
||||
ResetExprContext(econtext);
|
||||
|
||||
/* Make tuple and any needed join variables available to ExecProject */
|
||||
econtext->ecxt_scantuple = tupleSlot;
|
||||
if (tupleSlot)
|
||||
econtext->ecxt_scantuple = tupleSlot;
|
||||
else
|
||||
{
|
||||
HeapTuple tuple;
|
||||
|
||||
/*
|
||||
* RETURNING expressions might reference the tableoid column, so
|
||||
* initialize t_tableOid before evaluating them.
|
||||
*/
|
||||
Assert(!TupIsNull(econtext->ecxt_scantuple));
|
||||
tuple = ExecMaterializeSlot(econtext->ecxt_scantuple);
|
||||
tuple->t_tableOid = RelationGetRelid(resultRelInfo->ri_RelationDesc);
|
||||
}
|
||||
econtext->ecxt_outertuple = planSlot;
|
||||
|
||||
/* Compute the RETURNING expressions */
|
||||
@ -496,8 +513,7 @@ ExecInsert(ModifyTableState *mtstate,
|
||||
|
||||
/* Process RETURNING if present */
|
||||
if (resultRelInfo->ri_projectReturning)
|
||||
return ExecProcessReturning(resultRelInfo->ri_projectReturning,
|
||||
slot, planSlot);
|
||||
return ExecProcessReturning(resultRelInfo, slot, planSlot);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
@ -738,8 +754,7 @@ ldelete:;
|
||||
ExecStoreTuple(&deltuple, slot, InvalidBuffer, false);
|
||||
}
|
||||
|
||||
rslot = ExecProcessReturning(resultRelInfo->ri_projectReturning,
|
||||
slot, planSlot);
|
||||
rslot = ExecProcessReturning(resultRelInfo, slot, planSlot);
|
||||
|
||||
/*
|
||||
* Before releasing the target tuple again, make sure rslot has a
|
||||
@ -1024,8 +1039,7 @@ lreplace:;
|
||||
|
||||
/* Process RETURNING if present */
|
||||
if (resultRelInfo->ri_projectReturning)
|
||||
return ExecProcessReturning(resultRelInfo->ri_projectReturning,
|
||||
slot, planSlot);
|
||||
return ExecProcessReturning(resultRelInfo, slot, planSlot);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
@ -1380,6 +1394,26 @@ ExecModifyTable(ModifyTableState *node)
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* If resultRelInfo->ri_usesFdwDirectModify is true, all we need to do
|
||||
* here is compute the RETURNING expressions.
|
||||
*/
|
||||
if (resultRelInfo->ri_usesFdwDirectModify)
|
||||
{
|
||||
Assert(resultRelInfo->ri_projectReturning);
|
||||
|
||||
/*
|
||||
* A scan slot containing the data that was actually inserted,
|
||||
* updated or deleted has already been made available to
|
||||
* ExecProcessReturning by IterateDirectModify, so no need to
|
||||
* provide it here.
|
||||
*/
|
||||
slot = ExecProcessReturning(resultRelInfo, NULL, planSlot);
|
||||
|
||||
estate->es_result_relation_info = saved_resultRelInfo;
|
||||
return slot;
|
||||
}
|
||||
|
||||
EvalPlanQualSetSlot(&node->mt_epqstate, planSlot);
|
||||
slot = planSlot;
|
||||
|
||||
@ -1559,6 +1593,10 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
|
||||
{
|
||||
subplan = (Plan *) lfirst(l);
|
||||
|
||||
/* Initialize the usesFdwDirectModify flag */
|
||||
resultRelInfo->ri_usesFdwDirectModify = bms_is_member(i,
|
||||
node->fdwDirectModifyPlans);
|
||||
|
||||
/*
|
||||
* Verify result relation is a valid target for the current operation
|
||||
*/
|
||||
@ -1583,7 +1621,8 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
|
||||
mtstate->mt_plans[i] = ExecInitNode(subplan, estate, eflags);
|
||||
|
||||
/* Also let FDWs init themselves for foreign-table result rels */
|
||||
if (resultRelInfo->ri_FdwRoutine != NULL &&
|
||||
if (!resultRelInfo->ri_usesFdwDirectModify &&
|
||||
resultRelInfo->ri_FdwRoutine != NULL &&
|
||||
resultRelInfo->ri_FdwRoutine->BeginForeignModify != NULL)
|
||||
{
|
||||
List *fdw_private = (List *) list_nth(node->fdwPrivLists, i);
|
||||
@ -1910,7 +1949,8 @@ ExecEndModifyTable(ModifyTableState *node)
|
||||
{
|
||||
ResultRelInfo *resultRelInfo = node->resultRelInfo + i;
|
||||
|
||||
if (resultRelInfo->ri_FdwRoutine != NULL &&
|
||||
if (!resultRelInfo->ri_usesFdwDirectModify &&
|
||||
resultRelInfo->ri_FdwRoutine != NULL &&
|
||||
resultRelInfo->ri_FdwRoutine->EndForeignModify != NULL)
|
||||
resultRelInfo->ri_FdwRoutine->EndForeignModify(node->ps.state,
|
||||
resultRelInfo);
|
||||
|
@ -188,6 +188,7 @@ _copyModifyTable(const ModifyTable *from)
|
||||
COPY_NODE_FIELD(withCheckOptionLists);
|
||||
COPY_NODE_FIELD(returningLists);
|
||||
COPY_NODE_FIELD(fdwPrivLists);
|
||||
COPY_BITMAPSET_FIELD(fdwDirectModifyPlans);
|
||||
COPY_NODE_FIELD(rowMarks);
|
||||
COPY_SCALAR_FIELD(epqParam);
|
||||
COPY_SCALAR_FIELD(onConflictAction);
|
||||
@ -648,6 +649,7 @@ _copyForeignScan(const ForeignScan *from)
|
||||
/*
|
||||
* copy remainder of node
|
||||
*/
|
||||
COPY_SCALAR_FIELD(operation);
|
||||
COPY_SCALAR_FIELD(fs_server);
|
||||
COPY_NODE_FIELD(fdw_exprs);
|
||||
COPY_NODE_FIELD(fdw_private);
|
||||
|
@ -356,6 +356,7 @@ _outModifyTable(StringInfo str, const ModifyTable *node)
|
||||
WRITE_NODE_FIELD(withCheckOptionLists);
|
||||
WRITE_NODE_FIELD(returningLists);
|
||||
WRITE_NODE_FIELD(fdwPrivLists);
|
||||
WRITE_BITMAPSET_FIELD(fdwDirectModifyPlans);
|
||||
WRITE_NODE_FIELD(rowMarks);
|
||||
WRITE_INT_FIELD(epqParam);
|
||||
WRITE_ENUM_FIELD(onConflictAction, OnConflictAction);
|
||||
@ -608,6 +609,7 @@ _outForeignScan(StringInfo str, const ForeignScan *node)
|
||||
|
||||
_outScanInfo(str, (const Scan *) node);
|
||||
|
||||
WRITE_ENUM_FIELD(operation, CmdType);
|
||||
WRITE_OID_FIELD(fs_server);
|
||||
WRITE_NODE_FIELD(fdw_exprs);
|
||||
WRITE_NODE_FIELD(fdw_private);
|
||||
|
@ -1481,6 +1481,7 @@ _readModifyTable(void)
|
||||
READ_NODE_FIELD(withCheckOptionLists);
|
||||
READ_NODE_FIELD(returningLists);
|
||||
READ_NODE_FIELD(fdwPrivLists);
|
||||
READ_BITMAPSET_FIELD(fdwDirectModifyPlans);
|
||||
READ_NODE_FIELD(rowMarks);
|
||||
READ_INT_FIELD(epqParam);
|
||||
READ_ENUM_FIELD(onConflictAction, OnConflictAction);
|
||||
|
@ -4906,6 +4906,7 @@ make_foreignscan(List *qptlist,
|
||||
plan->lefttree = outer_plan;
|
||||
plan->righttree = NULL;
|
||||
node->scan.scanrelid = scanrelid;
|
||||
node->operation = CMD_SELECT;
|
||||
/* fs_server will be filled in by create_foreignscan_plan */
|
||||
node->fs_server = InvalidOid;
|
||||
node->fdw_exprs = fdw_exprs;
|
||||
@ -6021,6 +6022,7 @@ make_modifytable(PlannerInfo *root,
|
||||
{
|
||||
ModifyTable *node = makeNode(ModifyTable);
|
||||
List *fdw_private_list;
|
||||
Bitmapset *direct_modify_plans;
|
||||
ListCell *lc;
|
||||
int i;
|
||||
|
||||
@ -6078,12 +6080,14 @@ make_modifytable(PlannerInfo *root,
|
||||
* construct private plan data, and accumulate it all into a list.
|
||||
*/
|
||||
fdw_private_list = NIL;
|
||||
direct_modify_plans = NULL;
|
||||
i = 0;
|
||||
foreach(lc, resultRelations)
|
||||
{
|
||||
Index rti = lfirst_int(lc);
|
||||
FdwRoutine *fdwroutine;
|
||||
List *fdw_private;
|
||||
bool direct_modify;
|
||||
|
||||
/*
|
||||
* If possible, we want to get the FdwRoutine from our RelOptInfo for
|
||||
@ -6110,7 +6114,23 @@ make_modifytable(PlannerInfo *root,
|
||||
fdwroutine = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* If the target foreign table has any row-level triggers, we can't
|
||||
* modify the foreign table directly.
|
||||
*/
|
||||
direct_modify = false;
|
||||
if (fdwroutine != NULL &&
|
||||
fdwroutine->PlanDirectModify != NULL &&
|
||||
fdwroutine->BeginDirectModify != NULL &&
|
||||
fdwroutine->IterateDirectModify != NULL &&
|
||||
fdwroutine->EndDirectModify != NULL &&
|
||||
!has_row_triggers(root, rti, operation))
|
||||
direct_modify = fdwroutine->PlanDirectModify(root, node, rti, i);
|
||||
if (direct_modify)
|
||||
direct_modify_plans = bms_add_member(direct_modify_plans, i);
|
||||
|
||||
if (!direct_modify &&
|
||||
fdwroutine != NULL &&
|
||||
fdwroutine->PlanForeignModify != NULL)
|
||||
fdw_private = fdwroutine->PlanForeignModify(root, node, rti, i);
|
||||
else
|
||||
@ -6119,6 +6139,7 @@ make_modifytable(PlannerInfo *root,
|
||||
i++;
|
||||
}
|
||||
node->fdwPrivLists = fdw_private_list;
|
||||
node->fdwDirectModifyPlans = direct_modify_plans;
|
||||
|
||||
return node;
|
||||
}
|
||||
|
@ -1540,3 +1540,50 @@ has_unique_index(RelOptInfo *rel, AttrNumber attno)
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* has_row_triggers
|
||||
*
|
||||
* Detect whether the specified relation has any row-level triggers for event.
|
||||
*/
|
||||
bool
|
||||
has_row_triggers(PlannerInfo *root, Index rti, CmdType event)
|
||||
{
|
||||
RangeTblEntry *rte = planner_rt_fetch(rti, root);
|
||||
Relation relation;
|
||||
TriggerDesc *trigDesc;
|
||||
bool result = false;
|
||||
|
||||
/* Assume we already have adequate lock */
|
||||
relation = heap_open(rte->relid, NoLock);
|
||||
|
||||
trigDesc = relation->trigdesc;
|
||||
switch (event)
|
||||
{
|
||||
case CMD_INSERT:
|
||||
if (trigDesc &&
|
||||
(trigDesc->trig_insert_after_row ||
|
||||
trigDesc->trig_insert_before_row))
|
||||
result = true;
|
||||
break;
|
||||
case CMD_UPDATE:
|
||||
if (trigDesc &&
|
||||
(trigDesc->trig_update_after_row ||
|
||||
trigDesc->trig_update_before_row))
|
||||
result = true;
|
||||
break;
|
||||
case CMD_DELETE:
|
||||
if (trigDesc &&
|
||||
(trigDesc->trig_delete_after_row ||
|
||||
trigDesc->trig_delete_before_row))
|
||||
result = true;
|
||||
break;
|
||||
default:
|
||||
elog(ERROR, "unrecognized CmdType: %d", (int) event);
|
||||
break;
|
||||
}
|
||||
|
||||
heap_close(relation, NoLock);
|
||||
return result;
|
||||
}
|
||||
|
@ -97,6 +97,18 @@ typedef void (*EndForeignModify_function) (EState *estate,
|
||||
|
||||
typedef int (*IsForeignRelUpdatable_function) (Relation rel);
|
||||
|
||||
typedef bool (*PlanDirectModify_function) (PlannerInfo *root,
|
||||
ModifyTable *plan,
|
||||
Index resultRelation,
|
||||
int subplan_index);
|
||||
|
||||
typedef void (*BeginDirectModify_function) (ForeignScanState *node,
|
||||
int eflags);
|
||||
|
||||
typedef TupleTableSlot *(*IterateDirectModify_function) (ForeignScanState *node);
|
||||
|
||||
typedef void (*EndDirectModify_function) (ForeignScanState *node);
|
||||
|
||||
typedef RowMarkType (*GetForeignRowMarkType_function) (RangeTblEntry *rte,
|
||||
LockClauseStrength strength);
|
||||
|
||||
@ -114,6 +126,9 @@ typedef void (*ExplainForeignModify_function) (ModifyTableState *mtstate,
|
||||
int subplan_index,
|
||||
struct ExplainState *es);
|
||||
|
||||
typedef void (*ExplainDirectModify_function) (ForeignScanState *node,
|
||||
struct ExplainState *es);
|
||||
|
||||
typedef int (*AcquireSampleRowsFunc) (Relation relation, int elevel,
|
||||
HeapTuple *rows, int targrows,
|
||||
double *totalrows,
|
||||
@ -181,6 +196,10 @@ typedef struct FdwRoutine
|
||||
ExecForeignDelete_function ExecForeignDelete;
|
||||
EndForeignModify_function EndForeignModify;
|
||||
IsForeignRelUpdatable_function IsForeignRelUpdatable;
|
||||
PlanDirectModify_function PlanDirectModify;
|
||||
BeginDirectModify_function BeginDirectModify;
|
||||
IterateDirectModify_function IterateDirectModify;
|
||||
EndDirectModify_function EndDirectModify;
|
||||
|
||||
/* Functions for SELECT FOR UPDATE/SHARE row locking */
|
||||
GetForeignRowMarkType_function GetForeignRowMarkType;
|
||||
@ -190,6 +209,7 @@ typedef struct FdwRoutine
|
||||
/* Support functions for EXPLAIN */
|
||||
ExplainForeignScan_function ExplainForeignScan;
|
||||
ExplainForeignModify_function ExplainForeignModify;
|
||||
ExplainDirectModify_function ExplainDirectModify;
|
||||
|
||||
/* Support functions for ANALYZE */
|
||||
AnalyzeForeignTable_function AnalyzeForeignTable;
|
||||
|
@ -311,6 +311,7 @@ typedef struct JunkFilter
|
||||
* TrigInstrument optional runtime measurements for triggers
|
||||
* FdwRoutine FDW callback functions, if foreign table
|
||||
* FdwState available to save private state of FDW
|
||||
* usesFdwDirectModify true when modifying foreign table directly
|
||||
* WithCheckOptions list of WithCheckOption's to be checked
|
||||
* WithCheckOptionExprs list of WithCheckOption expr states
|
||||
* ConstraintExprs array of constraint-checking expr states
|
||||
@ -334,6 +335,7 @@ typedef struct ResultRelInfo
|
||||
Instrumentation *ri_TrigInstrument;
|
||||
struct FdwRoutine *ri_FdwRoutine;
|
||||
void *ri_FdwState;
|
||||
bool ri_usesFdwDirectModify;
|
||||
List *ri_WithCheckOptions;
|
||||
List *ri_WithCheckOptionExprs;
|
||||
List **ri_ConstraintExprs;
|
||||
|
@ -134,16 +134,19 @@ list_length(const List *l)
|
||||
#define list_make2(x1,x2) lcons(x1, list_make1(x2))
|
||||
#define list_make3(x1,x2,x3) lcons(x1, list_make2(x2, x3))
|
||||
#define list_make4(x1,x2,x3,x4) lcons(x1, list_make3(x2, x3, x4))
|
||||
#define list_make5(x1,x2,x3,x4,x5) lcons(x1, list_make4(x2, x3, x4, x5))
|
||||
|
||||
#define list_make1_int(x1) lcons_int(x1, NIL)
|
||||
#define list_make2_int(x1,x2) lcons_int(x1, list_make1_int(x2))
|
||||
#define list_make3_int(x1,x2,x3) lcons_int(x1, list_make2_int(x2, x3))
|
||||
#define list_make4_int(x1,x2,x3,x4) lcons_int(x1, list_make3_int(x2, x3, x4))
|
||||
#define list_make5_int(x1,x2,x3,x4,x5) lcons_int(x1, list_make4_int(x2, x3, x4, x5))
|
||||
|
||||
#define list_make1_oid(x1) lcons_oid(x1, NIL)
|
||||
#define list_make2_oid(x1,x2) lcons_oid(x1, list_make1_oid(x2))
|
||||
#define list_make3_oid(x1,x2,x3) lcons_oid(x1, list_make2_oid(x2, x3))
|
||||
#define list_make4_oid(x1,x2,x3,x4) lcons_oid(x1, list_make3_oid(x2, x3, x4))
|
||||
#define list_make5_oid(x1,x2,x3,x4,x5) lcons_oid(x1, list_make4_oid(x2, x3, x4, x5))
|
||||
|
||||
/*
|
||||
* foreach -
|
||||
|
@ -189,6 +189,7 @@ typedef struct ModifyTable
|
||||
List *withCheckOptionLists; /* per-target-table WCO lists */
|
||||
List *returningLists; /* per-target-table RETURNING tlists */
|
||||
List *fdwPrivLists; /* per-target-table FDW private data lists */
|
||||
Bitmapset *fdwDirectModifyPlans; /* indices of FDW DM plans */
|
||||
List *rowMarks; /* PlanRowMarks (non-locking only) */
|
||||
int epqParam; /* ID of Param for EvalPlanQual re-eval */
|
||||
OnConflictAction onConflictAction; /* ON CONFLICT action */
|
||||
@ -531,6 +532,7 @@ typedef struct WorkTableScan
|
||||
typedef struct ForeignScan
|
||||
{
|
||||
Scan scan;
|
||||
CmdType operation; /* SELECT/INSERT/UPDATE/DELETE */
|
||||
Oid fs_server; /* OID of foreign server */
|
||||
List *fdw_exprs; /* expressions that FDW may evaluate */
|
||||
List *fdw_private; /* private data for FDW */
|
||||
|
@ -55,4 +55,6 @@ extern Selectivity join_selectivity(PlannerInfo *root,
|
||||
JoinType jointype,
|
||||
SpecialJoinInfo *sjinfo);
|
||||
|
||||
extern bool has_row_triggers(PlannerInfo *root, Index rti, CmdType event);
|
||||
|
||||
#endif /* PLANCAT_H */
|
||||
|
Reference in New Issue
Block a user