Hi hackers,
While working with cursors that reference plans with CustomScanStates
nodes, I encountered a segfault which originates from
search_plan_tree(). The query plan is the result of a simple SELECT
statement into which I inject a Custom Scan node at the root to do some
post-processing before returning rows. This plan is referenced by a
second plan with a Tid Scan which originates from a query of the form
DELETE FROM foo WHERE CURRENT OF my_cursor;
search_plan_tree() assumes that
CustomScanState::ScanState::ss_currentRelation is never NULL. In my
understanding that only holds for CustomScanState nodes which are at the
bottom of the plan and actually read from a relation. CustomScanState
nodes which are not at the bottom don't have ss_currentRelation set. I
believe for such nodes, instead search_plan_tree() should recurse into
CustomScanState::custom_ps.
I attached a patch. Any thoughts?
Best regards,
David
Swarm64
diff --git a/src/backend/executor/execCurrent.c
b/src/backend/executor/execCurrent.c
index f89319fcd8..0d5f09402b 100644
--- a/src/backend/executor/execCurrent.c
+++ b/src/backend/executor/execCurrent.c
@@ -326,7 +326,6 @@ search_plan_tree(PlanState *node, Oid table_oid,
case T_BitmapHeapScanState:
case T_TidScanState:
case T_ForeignScanState:
- case T_CustomScanState:
{
ScanState *sstate = (ScanState *) node;
@@ -335,6 +334,39 @@ search_plan_tree(PlanState *node, Oid table_oid,
break;
}
+
+ /*
+ * Custom scan nodes can be leaf nodes or inner nodes
and therfore need special treatment.
+ */
+ case T_CustomScanState:
+ {
+ CustomScanState *css =
castNode(CustomScanState, node);
+ ScanState *sstate = (ScanState *) node;
+
+ if (sstate->ss_currentRelation == NULL) /*
inner node */
+ {
+ ListCell *lc;
+
+ foreach (lc, sstate->custom_ps)
+ {
+ ScanState *elem =
search_plan_tree((PlanState *)lfirst(lc), table_oid, pending_rescan);
+
+ if (!elem)
+ continue;
+ if (result)
+ return NULL; /*
multiple matches */
+ result = elem;
+ }
+ }
+ else /* leaf node */
+ {
+ if
(RelationGetRelid(sstate->ss_currentRelation) == table_oid)
+ result = sstate;
+ }
+
+ break;
+ }
+
/*
* For Append, we must look through the members; watch
out for
* multiple matches (possible if it was from UNION ALL)