From c3d1a8ab4d1864d15b3605e3d42b8f1ab7e0ad15 Mon Sep 17 00:00:00 2001
From: Denis Hirn <denis.hirn@uni-tuebingen.de>
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

