Skip to content

Commit 5d56d07

Browse files
committed
Optimize tuplestore usage for WITH RECURSIVE CTEs
nodeRecursiveunion.c makes use of two tuplestores and, until now, would delete and recreate one of these tuplestores after every recursive iteration. Here we adjust that behavior and instead reuse one of the existing tuplestores and just empty it of all tuples using tuplestore_clear(). This saves some free/malloc roundtrips and has shown a 25-30% performance improvement for queries that perform very little work between recursive iterations. This also paves the way to add some EXPLAIN ANALYZE telemetry output for recursive common table expressions, similar to what was done in 1eff827 and 95d6e9a. Previously calling tuplestore_end() would have caused the maximum storage space used to be lost. Reviewed-by: Tatsuo Ishii Discussion: https://postgr.es/m/CAApHDvr9yW0YRiK8A2J7nvyT8g17YzbSfOviEWrghazKZbHbig@mail.gmail.com
1 parent 8a6e85b commit 5d56d07

File tree

1 file changed

+13
-6
lines changed

1 file changed

+13
-6
lines changed

src/backend/executor/nodeRecursiveunion.c

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -115,19 +115,26 @@ ExecRecursiveUnion(PlanState *pstate)
115115
slot = ExecProcNode(innerPlan);
116116
if (TupIsNull(slot))
117117
{
118+
Tuplestorestate *swaptemp;
119+
118120
/* Done if there's nothing in the intermediate table */
119121
if (node->intermediate_empty)
120122
break;
121123

122-
/* done with old working table ... */
123-
tuplestore_end(node->working_table);
124+
/*
125+
* Now we let the intermediate table become the work table. We
126+
* need a fresh intermediate table, so delete the tuples from the
127+
* current working table and use that as the new intermediate
128+
* table. This saves a round of free/malloc from creating a new
129+
* tuple store.
130+
*/
131+
tuplestore_clear(node->working_table);
124132

125-
/* intermediate table becomes working table */
133+
swaptemp = node->working_table;
126134
node->working_table = node->intermediate_table;
135+
node->intermediate_table = swaptemp;
127136

128-
/* create new empty intermediate table */
129-
node->intermediate_table = tuplestore_begin_heap(false, false,
130-
work_mem);
137+
/* mark the intermediate table as empty */
131138
node->intermediate_empty = true;
132139

133140
/* reset the recursive term */

0 commit comments

Comments
 (0)