mirror of
https://git.postgresql.org/git/postgresql.git
synced 2024-10-06 01:37:01 +02:00
Fix segfault during EvalPlanQual with mix of local and foreign partitions.
It's not sensible to re-evaluate a direct-modify Foreign Update or Delete
during EvalPlanQual. However, ExecInitForeignScan() can still get called
if a table mixes local and foreign partitions. EvalPlanQualStart() left
the es_result_relations array uninitialized in the child EPQ EState, but
ExecInitForeignScan() still expected to find it. That caused a segfault.
Fix by skipping the es_result_relations lookup during EvalPlanQual
processing. To make things a bit more robust, also skip the
BeginDirectModify calls, and add a runtime check that ExecForeignScan()
is not called on direct-modify foreign scans during EvalPlanQual
processing.
This is new in v14, commit 1375422c78
. Before that, EvalPlanQualStart()
copied the whole ResultRelInfo array to the EPQ EState. Backpatch to v14.
Report and diagnosis by Andrey Lepikhov.
Discussion: https://www.postgresql.org/message-id/cb2b808d-cbaa-4772-76ee-c8809bafcf3d%40postgrespro.ru
This commit is contained in:
parent
a4957b5a72
commit
6458ed18fe
@ -44,12 +44,22 @@ ForeignNext(ForeignScanState *node)
|
|||||||
TupleTableSlot *slot;
|
TupleTableSlot *slot;
|
||||||
ForeignScan *plan = (ForeignScan *) node->ss.ps.plan;
|
ForeignScan *plan = (ForeignScan *) node->ss.ps.plan;
|
||||||
ExprContext *econtext = node->ss.ps.ps_ExprContext;
|
ExprContext *econtext = node->ss.ps.ps_ExprContext;
|
||||||
|
EState *estate = node->ss.ps.state;
|
||||||
MemoryContext oldcontext;
|
MemoryContext oldcontext;
|
||||||
|
|
||||||
/* Call the Iterate function in short-lived context */
|
/* Call the Iterate function in short-lived context */
|
||||||
oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
|
oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
|
||||||
if (plan->operation != CMD_SELECT)
|
if (plan->operation != CMD_SELECT)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* direct modifications cannot be re-evaluated, so shouldn't get here
|
||||||
|
* during EvalPlanQual processing
|
||||||
|
*/
|
||||||
|
if (estate->es_epq_active != NULL)
|
||||||
|
elog(ERROR, "cannot re-evaluate a Foreign Update or Delete during EvalPlanQual");
|
||||||
|
|
||||||
slot = node->fdwroutine->IterateDirectModify(node);
|
slot = node->fdwroutine->IterateDirectModify(node);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
slot = node->fdwroutine->IterateForeignScan(node);
|
slot = node->fdwroutine->IterateForeignScan(node);
|
||||||
MemoryContextSwitchTo(oldcontext);
|
MemoryContextSwitchTo(oldcontext);
|
||||||
@ -223,11 +233,25 @@ ExecInitForeignScan(ForeignScan *node, EState *estate, int eflags)
|
|||||||
scanstate->fdw_state = NULL;
|
scanstate->fdw_state = NULL;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* For the FDW's convenience, look up the modification target relation's.
|
* For the FDW's convenience, look up the modification target relation's
|
||||||
* ResultRelInfo.
|
* ResultRelInfo. The ModifyTable node should have initialized it for us,
|
||||||
|
* see ExecInitModifyTable.
|
||||||
|
*
|
||||||
|
* Don't try to look up the ResultRelInfo when EvalPlanQual is active,
|
||||||
|
* though. Direct modififications cannot be re-evaluated as part of
|
||||||
|
* EvalPlanQual. The lookup wouldn't work anyway because during
|
||||||
|
* EvalPlanQual processing, EvalPlanQual only initializes the subtree
|
||||||
|
* under the ModifyTable, and doesn't run ExecInitModifyTable.
|
||||||
*/
|
*/
|
||||||
if (node->resultRelation > 0)
|
if (node->resultRelation > 0 && estate->es_epq_active == NULL)
|
||||||
|
{
|
||||||
|
if (estate->es_result_relations == NULL ||
|
||||||
|
estate->es_result_relations[node->resultRelation - 1] == NULL)
|
||||||
|
{
|
||||||
|
elog(ERROR, "result relation not initialized");
|
||||||
|
}
|
||||||
scanstate->resultRelInfo = estate->es_result_relations[node->resultRelation - 1];
|
scanstate->resultRelInfo = estate->es_result_relations[node->resultRelation - 1];
|
||||||
|
}
|
||||||
|
|
||||||
/* Initialize any outer plan. */
|
/* Initialize any outer plan. */
|
||||||
if (outerPlan(node))
|
if (outerPlan(node))
|
||||||
@ -238,7 +262,15 @@ ExecInitForeignScan(ForeignScan *node, EState *estate, int eflags)
|
|||||||
* Tell the FDW to initialize the scan.
|
* Tell the FDW to initialize the scan.
|
||||||
*/
|
*/
|
||||||
if (node->operation != CMD_SELECT)
|
if (node->operation != CMD_SELECT)
|
||||||
fdwroutine->BeginDirectModify(scanstate, eflags);
|
{
|
||||||
|
/*
|
||||||
|
* Direct modifications cannot be re-evaluated by EvalPlanQual, so
|
||||||
|
* don't bother preparing the FDW. There can ForeignScan nodes in the
|
||||||
|
* EvalPlanQual subtree, but ExecForeignScan should never be called.
|
||||||
|
*/
|
||||||
|
if (estate->es_epq_active == NULL)
|
||||||
|
fdwroutine->BeginDirectModify(scanstate, eflags);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
fdwroutine->BeginForeignScan(scanstate, eflags);
|
fdwroutine->BeginForeignScan(scanstate, eflags);
|
||||||
|
|
||||||
@ -255,10 +287,14 @@ void
|
|||||||
ExecEndForeignScan(ForeignScanState *node)
|
ExecEndForeignScan(ForeignScanState *node)
|
||||||
{
|
{
|
||||||
ForeignScan *plan = (ForeignScan *) node->ss.ps.plan;
|
ForeignScan *plan = (ForeignScan *) node->ss.ps.plan;
|
||||||
|
EState *estate = node->ss.ps.state;
|
||||||
|
|
||||||
/* Let the FDW shut down */
|
/* Let the FDW shut down */
|
||||||
if (plan->operation != CMD_SELECT)
|
if (plan->operation != CMD_SELECT)
|
||||||
node->fdwroutine->EndDirectModify(node);
|
{
|
||||||
|
if (estate->es_epq_active == NULL)
|
||||||
|
node->fdwroutine->EndDirectModify(node);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
node->fdwroutine->EndForeignScan(node);
|
node->fdwroutine->EndForeignScan(node);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user