On Thu, 19 Sept 2024 at 12:01, Tatsuo Ishii <[email protected]> wrote:
> > Could you add the two sizes together and take the storage type from
> > the tuplestore with the highest storage size?
>
> I don't think this works because tuplestore_begin/tuplestore_end are
> called while executing the node (ExecRecursiveUnion).
>
> I think the way you are proposing only shows the stats last time when
> those tuplestore are created.
That code could be modified to swap the tuplestores and do a
tuplestore_clear() instead of tuplestore_end() followed by
tuplestore_begin_heap().
It's likely worthwhile from a performance point of view. Here's a
small test as an example:
master:
postgres=# with recursive cte (a) as (select 1 union all select
cte.a+1 from cte where cte.a+1 <= 1000000) select count(*) from cte;
Time: 219.023 ms
Time: 218.828 ms
Time: 219.093 ms
with attached patched:
postgres=# with recursive cte (a) as (select 1 union all select
cte.a+1 from cte where cte.a+1 <= 1000000) select count(*) from cte;
Time: 169.734 ms
Time: 164.841 ms
Time: 169.168 ms
David
diff --git a/src/backend/executor/nodeRecursiveunion.c
b/src/backend/executor/nodeRecursiveunion.c
index c7f8a19fa4..8aa7a78d0c 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 */