On Thu, 19 Sept 2024 at 12:01, Tatsuo Ishii <is...@postgresql.org> 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 */

Reply via email to