diff options
author | David Rowley <drowley@postgresql.org> | 2024-09-19 15:20:35 +1200 |
---|---|---|
committer | David Rowley <drowley@postgresql.org> | 2024-09-19 15:20:35 +1200 |
commit | 5d56d07ca343a467ce74a042c22c963ea2690eaf (patch) | |
tree | 080c10e729c58c29b956d23efc46fed5cd570156 /src/backend/executor/nodeRecursiveunion.c | |
parent | 8a6e85b46e0ff1a49b1b2303149ec010e5e0b30e (diff) | |
download | postgresql-5d56d07ca343a467ce74a042c22c963ea2690eaf.tar.gz postgresql-5d56d07ca343a467ce74a042c22c963ea2690eaf.zip |
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 1eff8279d
and 95d6e9af0. 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
Diffstat (limited to 'src/backend/executor/nodeRecursiveunion.c')
-rw-r--r-- | src/backend/executor/nodeRecursiveunion.c | 19 |
1 files changed, 13 insertions, 6 deletions
diff --git a/src/backend/executor/nodeRecursiveunion.c b/src/backend/executor/nodeRecursiveunion.c index c7f8a19fa44..22e7b83b2e6 100644 --- a/src/backend/executor/nodeRecursiveunion.c +++ b/src/backend/executor/nodeRecursiveunion.c @@ -115,19 +115,26 @@ ExecRecursiveUnion(PlanState *pstate) slot = ExecProcNode(innerPlan); if (TupIsNull(slot)) { + Tuplestorestate *swaptemp; + /* Done if there's nothing in the intermediate table */ if (node->intermediate_empty) break; - /* done with old working table ... */ - tuplestore_end(node->working_table); + /* + * Now we let the intermediate table become the work table. We + * need a fresh intermediate table, so delete the tuples from the + * current working table and use that as the new intermediate + * table. This saves a round of free/malloc from creating a new + * tuple store. + */ + tuplestore_clear(node->working_table); - /* intermediate table becomes working table */ + swaptemp = node->working_table; node->working_table = node->intermediate_table; + node->intermediate_table = swaptemp; - /* create new empty intermediate table */ - node->intermediate_table = tuplestore_begin_heap(false, false, - work_mem); + /* mark the intermediate table as empty */ node->intermediate_empty = true; /* reset the recursive term */ |