From c3d1a8ab4d1864d15b3605e3d42b8f1ab7e0ad15 Mon Sep 17 00:00:00 2001 From: Denis Hirn Date: Tue, 23 Mar 2021 12:58:36 +0100 Subject: [PATCH] Allow multiple recursive self-references --- postgres/src/backend/executor/nodeWorktablescan.c | 13 +++++++------ postgres/src/backend/parser/parse_cte.c | 10 ++-------- postgres/src/include/nodes/execnodes.h | 1 + 3 files changed, 10 insertions(+), 14 deletions(-) diff --git a/postgres/src/backend/executor/nodeWorktablescan.c b/postgres/src/backend/executor/nodeWorktablescan.c index e8f5caf..b2e1423 100644 --- a/postgres/src/backend/executor/nodeWorktablescan.c +++ b/postgres/src/backend/executor/nodeWorktablescan.c @@ -42,10 +42,6 @@ WorkTableScanNext(WorkTableScanState *node) * worktable plan node, since it cannot appear high enough in the plan * tree of a scrollable cursor to be exposed to a backward-scan * requirement. So it's not worth expending effort to support it. - * - * Note: we are also assuming that this node is the only reader of the - * worktable. Therefore, we don't need a private read pointer for the - * tuplestore, nor do we need to tell tuplestore_gettupleslot to copy. */ Assert(ScanDirectionIsForward(node->ss.ps.state->es_direction)); @@ -55,6 +51,7 @@ WorkTableScanNext(WorkTableScanState *node) * Get the next tuple from tuplestore. Return NULL if no more tuples. */ slot = node->ss.ss_ScanTupleSlot; + tuplestore_select_read_pointer(tuplestorestate, node->readptr); (void) tuplestore_gettupleslot(tuplestorestate, true, false, slot); return slot; } @@ -99,7 +96,8 @@ ExecWorkTableScan(PlanState *pstate) Assert(!param->isnull); node->rustate = castNode(RecursiveUnionState, DatumGetPointer(param->value)); Assert(node->rustate); - + node->readptr = tuplestore_alloc_read_pointer(node->rustate->working_table, 0); + Assert(node->readptr != -1); /* * The scan tuple type (ie, the rowtype we expect to find in the work * table) is the same as the result rowtype of the ancestor @@ -147,6 +145,7 @@ ExecInitWorkTableScan(WorkTableScan *node, EState *estate, int eflags) scanstate->ss.ps.plan = (Plan *) node; scanstate->ss.ps.state = estate; scanstate->ss.ps.ExecProcNode = ExecWorkTableScan; + scanstate->readptr = -1; /* we'll see this later */ scanstate->rustate = NULL; /* we'll set this later */ /* @@ -218,6 +217,8 @@ ExecReScanWorkTableScan(WorkTableScanState *node) ExecScanReScan(&node->ss); /* No need (or way) to rescan if ExecWorkTableScan not called yet */ - if (node->rustate) + if (node->rustate) { + if(node->readptr != -1) tuplestore_select_read_pointer(node->rustate->working_table, node->readptr); tuplestore_rescan(node->rustate->working_table); + } } diff --git a/postgres/src/backend/parser/parse_cte.c b/postgres/src/backend/parser/parse_cte.c index 1fca748..5ab8310 100644 --- a/postgres/src/backend/parser/parse_cte.c +++ b/postgres/src/backend/parser/parse_cte.c @@ -674,7 +674,7 @@ checkWellFormedRecursion(CteState *cstate) cstate->context = RECURSION_OK; checkWellFormedRecursionWalker((Node *) stmt->rarg, cstate); Assert(cstate->innerwiths == NIL); - if (cstate->selfrefcount != 1) /* shouldn't happen */ + if (cstate->selfrefcount < 1) /* shouldn't happen */ elog(ERROR, "missing recursive reference"); /* WITH mustn't contain self-reference, either */ @@ -771,13 +771,7 @@ checkWellFormedRecursionWalker(Node *node, CteState *cstate) parser_errposition(cstate->pstate, rv->location))); /* Count references */ - if (++(cstate->selfrefcount) > 1) - ereport(ERROR, - (errcode(ERRCODE_INVALID_RECURSION), - errmsg("recursive reference to query \"%s\" must not appear more than once", - mycte->ctename), - parser_errposition(cstate->pstate, - rv->location))); + ++(cstate->selfrefcount); } } return false; diff --git a/postgres/src/include/nodes/execnodes.h b/postgres/src/include/nodes/execnodes.h index 015bfc0..56e4ce3 100644 --- a/postgres/src/include/nodes/execnodes.h +++ b/postgres/src/include/nodes/execnodes.h @@ -1782,6 +1782,7 @@ typedef struct NamedTuplestoreScanState typedef struct WorkTableScanState { ScanState ss; /* its first field is NodeTag */ + int readptr; /* indexof my tuplestore read pointer */ RecursiveUnionState *rustate; } WorkTableScanState; -- 2.30.1