summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHeikki Linnakangas <heikki.linnakangas@iki.fi>2021-08-12 11:02:29 +0300
committerHeikki Linnakangas <heikki.linnakangas@iki.fi>2021-08-12 11:02:29 +0300
commitc3928b467a4f0ed2b0ef21a33848e9fcdade37b4 (patch)
tree0cf2bc5b6b0ba94af4583c30898d5624260ca0d8
parenta6bd28beb0639d4cf424e961862a65c466ca65bf (diff)
downloadpostgresql-c3928b467a4f0ed2b0ef21a33848e9fcdade37b4.tar.gz
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 1375422c782. 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
-rw-r--r--src/backend/executor/nodeForeignscan.c46
1 files changed, 41 insertions, 5 deletions
diff --git a/src/backend/executor/nodeForeignscan.c b/src/backend/executor/nodeForeignscan.c
index 9dc38d47ea..ad9edd26f1 100644
--- a/src/backend/executor/nodeForeignscan.c
+++ b/src/backend/executor/nodeForeignscan.c
@@ -44,12 +44,22 @@ ForeignNext(ForeignScanState *node)
TupleTableSlot *slot;
ForeignScan *plan = (ForeignScan *) node->ss.ps.plan;
ExprContext *econtext = node->ss.ps.ps_ExprContext;
+ EState *estate = node->ss.ps.state;
MemoryContext oldcontext;
/* Call the Iterate function in short-lived context */
oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
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);
+ }
else
slot = node->fdwroutine->IterateForeignScan(node);
MemoryContextSwitchTo(oldcontext);
@@ -223,11 +233,25 @@ ExecInitForeignScan(ForeignScan *node, EState *estate, int eflags)
scanstate->fdw_state = NULL;
/*
- * For the FDW's convenience, look up the modification target relation's.
- * ResultRelInfo.
+ * For the FDW's convenience, look up the modification target relation's
+ * 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];
+ }
/* Initialize any outer plan. */
if (outerPlan(node))
@@ -238,7 +262,15 @@ ExecInitForeignScan(ForeignScan *node, EState *estate, int eflags)
* Tell the FDW to initialize the scan.
*/
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
fdwroutine->BeginForeignScan(scanstate, eflags);
@@ -255,10 +287,14 @@ void
ExecEndForeignScan(ForeignScanState *node)
{
ForeignScan *plan = (ForeignScan *) node->ss.ps.plan;
+ EState *estate = node->ss.ps.state;
/* Let the FDW shut down */
if (plan->operation != CMD_SELECT)
- node->fdwroutine->EndDirectModify(node);
+ {
+ if (estate->es_epq_active == NULL)
+ node->fdwroutine->EndDirectModify(node);
+ }
else
node->fdwroutine->EndForeignScan(node);