LCOV - differential code coverage report
Current view: top level - src/backend/executor - nodeRecursiveunion.c (source / functions) Coverage Total Hit UBC CBC
Current: Differential Code Coverage HEAD vs 15 Lines: 99.0 % 105 104 1 104
Current Date: 2023-04-08 17:13:01 Functions: 100.0 % 5 5 5
Baseline: 15 Line coverage date bins:
Baseline Date: 2023-04-08 15:09:40 (240..) days: 99.0 % 105 104 1 104
Legend: Lines: hit not hit Function coverage date bins:
(240..) days: 100.0 % 5 5 5

 Age         Owner                  TLA  Line data    Source code
                                  1                 : /*-------------------------------------------------------------------------
                                  2                 :  *
                                  3                 :  * nodeRecursiveunion.c
                                  4                 :  *    routines to handle RecursiveUnion nodes.
                                  5                 :  *
                                  6                 :  * To implement UNION (without ALL), we need a hashtable that stores tuples
                                  7                 :  * already seen.  The hash key is computed from the grouping columns.
                                  8                 :  *
                                  9                 :  *
                                 10                 :  * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group
                                 11                 :  * Portions Copyright (c) 1994, Regents of the University of California
                                 12                 :  *
                                 13                 :  *
                                 14                 :  * IDENTIFICATION
                                 15                 :  *    src/backend/executor/nodeRecursiveunion.c
                                 16                 :  *
                                 17                 :  *-------------------------------------------------------------------------
                                 18                 :  */
                                 19                 : #include "postgres.h"
                                 20                 : 
                                 21                 : #include "executor/execdebug.h"
                                 22                 : #include "executor/nodeRecursiveunion.h"
                                 23                 : #include "miscadmin.h"
                                 24                 : #include "utils/memutils.h"
                                 25                 : 
                                 26                 : 
                                 27                 : 
                                 28                 : /*
                                 29                 :  * Initialize the hash table to empty.
                                 30                 :  */
                                 31                 : static void
 5297 tgl                        32 CBC         144 : build_hash_table(RecursiveUnionState *rustate)
                                 33                 : {
 5050 bruce                      34             144 :     RecursiveUnion *node = (RecursiveUnion *) rustate->ps.plan;
 1879 andres                     35             144 :     TupleDesc   desc = ExecGetResultType(outerPlanState(rustate));
                                 36                 : 
 5297 tgl                        37             144 :     Assert(node->numCols > 0);
                                 38             144 :     Assert(node->numGroups > 0);
                                 39                 : 
 1520 andres                     40             288 :     rustate->hashtable = BuildTupleHashTableExt(&rustate->ps,
                                 41                 :                                                 desc,
                                 42                 :                                                 node->numCols,
                                 43                 :                                                 node->dupColIdx,
                                 44             144 :                                                 rustate->eqfuncoids,
                                 45                 :                                                 rustate->hashfunctions,
                                 46                 :                                                 node->dupCollations,
                                 47                 :                                                 node->numGroups,
                                 48                 :                                                 0,
                                 49             144 :                                                 rustate->ps.state->es_query_cxt,
                                 50                 :                                                 rustate->tableContext,
                                 51                 :                                                 rustate->tempContext,
                                 52                 :                                                 false);
 5297 tgl                        53             144 : }
                                 54                 : 
                                 55                 : 
                                 56                 : /* ----------------------------------------------------------------
                                 57                 :  *      ExecRecursiveUnion(node)
                                 58                 :  *
                                 59                 :  *      Scans the recursive query sequentially and returns the next
                                 60                 :  *      qualifying tuple.
                                 61                 :  *
                                 62                 :  * 1. evaluate non recursive term and assign the result to RT
                                 63                 :  *
                                 64                 :  * 2. execute recursive terms
                                 65                 :  *
                                 66                 :  * 2.1 WT := RT
                                 67                 :  * 2.2 while WT is not empty repeat 2.3 to 2.6. if WT is empty returns RT
                                 68                 :  * 2.3 replace the name of recursive term with WT
                                 69                 :  * 2.4 evaluate the recursive term and store into WT
                                 70                 :  * 2.5 append WT to RT
                                 71                 :  * 2.6 go back to 2.2
                                 72                 :  * ----------------------------------------------------------------
                                 73                 :  */
                                 74                 : static TupleTableSlot *
 2092 andres                     75           18189 : ExecRecursiveUnion(PlanState *pstate)
                                 76                 : {
                                 77           18189 :     RecursiveUnionState *node = castNode(RecursiveUnionState, pstate);
 5300 tgl                        78           18189 :     PlanState  *outerPlan = outerPlanState(node);
                                 79           18189 :     PlanState  *innerPlan = innerPlanState(node);
                                 80           18189 :     RecursiveUnion *plan = (RecursiveUnion *) node->ps.plan;
                                 81                 :     TupleTableSlot *slot;
                                 82                 :     bool        isnew;
                                 83                 : 
 2084 andres                     84           18189 :     CHECK_FOR_INTERRUPTS();
                                 85                 : 
                                 86                 :     /* 1. Evaluate non-recursive term */
 5300 tgl                        87           18189 :     if (!node->recursing)
                                 88                 :     {
                                 89                 :         for (;;)
                                 90                 :         {
 5297                            91            3763 :             slot = ExecProcNode(outerPlan);
                                 92            3763 :             if (TupIsNull(slot))
                                 93                 :                 break;
                                 94            3433 :             if (plan->numCols > 0)
                                 95                 :             {
                                 96                 :                 /* Find or build hashtable entry for this tuple's group */
  987 jdavis                     97             243 :                 LookupTupleHashEntry(node->hashtable, slot, &isnew, NULL);
                                 98                 :                 /* Must reset temp context after each hashtable lookup */
 5297 tgl                        99             243 :                 MemoryContextReset(node->tempContext);
                                100                 :                 /* Ignore tuple if already seen */
                                101             243 :                 if (!isnew)
                                102               4 :                     continue;
                                103                 :             }
                                104                 :             /* Each non-duplicate tuple goes to the working table ... */
 5300                           105            3429 :             tuplestore_puttupleslot(node->working_table, slot);
                                106                 :             /* ... and to the caller */
                                107            3429 :             return slot;
                                108                 :         }
                                109             330 :         node->recursing = true;
                                110                 :     }
                                111                 : 
                                112                 :     /* 2. Execute recursive term */
                                113                 :     for (;;)
                                114                 :     {
 5297                           115           17892 :         slot = ExecProcNode(innerPlan);
                                116           17892 :         if (TupIsNull(slot))
                                117                 :         {
                                118                 :             /* Done if there's nothing in the intermediate table */
                                119            3407 :             if (node->intermediate_empty)
                                120             309 :                 break;
                                121                 : 
                                122                 :             /* done with old working table ... */
                                123            3098 :             tuplestore_end(node->working_table);
                                124                 : 
                                125                 :             /* intermediate table becomes working table */
                                126            3098 :             node->working_table = node->intermediate_table;
                                127                 : 
                                128                 :             /* create new empty intermediate table */
                                129            3098 :             node->intermediate_table = tuplestore_begin_heap(false, false,
                                130                 :                                                              work_mem);
                                131            3098 :             node->intermediate_empty = true;
                                132                 : 
                                133                 :             /* reset the recursive term */
                                134            3098 :             innerPlan->chgParam = bms_add_member(innerPlan->chgParam,
                                135                 :                                                  plan->wtParam);
                                136                 : 
                                137                 :             /* and continue fetching from recursive term */
                                138            3098 :             continue;
                                139                 :         }
                                140                 : 
                                141           14485 :         if (plan->numCols > 0)
                                142                 :         {
                                143                 :             /* Find or build hashtable entry for this tuple's group */
  987 jdavis                    144             438 :             LookupTupleHashEntry(node->hashtable, slot, &isnew, NULL);
                                145                 :             /* Must reset temp context after each hashtable lookup */
 5297 tgl                       146             438 :             MemoryContextReset(node->tempContext);
                                147                 :             /* Ignore tuple if already seen */
                                148             438 :             if (!isnew)
                                149              34 :                 continue;
                                150                 :         }
                                151                 : 
                                152                 :         /* Else, tuple is good; stash it in intermediate table ... */
 5050 bruce                     153           14451 :         node->intermediate_empty = false;
                                154           14451 :         tuplestore_puttupleslot(node->intermediate_table, slot);
                                155                 :         /* ... and return it */
 5297 tgl                       156           14451 :         return slot;
                                157                 :     }
                                158                 : 
                                159             309 :     return NULL;
                                160                 : }
                                161                 : 
                                162                 : /* ----------------------------------------------------------------
                                163                 :  *      ExecInitRecursiveUnion
                                164                 :  * ----------------------------------------------------------------
                                165                 :  */
                                166                 : RecursiveUnionState *
 5300                           167             354 : ExecInitRecursiveUnion(RecursiveUnion *node, EState *estate, int eflags)
                                168                 : {
                                169                 :     RecursiveUnionState *rustate;
                                170                 :     ParamExecData *prmdata;
                                171                 : 
                                172                 :     /* check for unsupported flags */
                                173             354 :     Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)));
                                174                 : 
                                175                 :     /*
                                176                 :      * create state structure
                                177                 :      */
                                178             354 :     rustate = makeNode(RecursiveUnionState);
                                179             354 :     rustate->ps.plan = (Plan *) node;
                                180             354 :     rustate->ps.state = estate;
 2092 andres                    181             354 :     rustate->ps.ExecProcNode = ExecRecursiveUnion;
                                182                 : 
 1879                           183             354 :     rustate->eqfuncoids = NULL;
 5297 tgl                       184             354 :     rustate->hashfunctions = NULL;
                                185             354 :     rustate->hashtable = NULL;
                                186             354 :     rustate->tempContext = NULL;
                                187             354 :     rustate->tableContext = NULL;
                                188                 : 
                                189                 :     /* initialize processing state */
 5300                           190             354 :     rustate->recursing = false;
                                191             354 :     rustate->intermediate_empty = true;
                                192             354 :     rustate->working_table = tuplestore_begin_heap(false, false, work_mem);
                                193             354 :     rustate->intermediate_table = tuplestore_begin_heap(false, false, work_mem);
                                194                 : 
                                195                 :     /*
                                196                 :      * If hashing, we need a per-tuple memory context for comparisons, and a
                                197                 :      * longer-lived context to store the hash table.  The table can't just be
                                198                 :      * kept in the per-query context because we want to be able to throw it
                                199                 :      * away when rescanning.
                                200                 :      */
 5297                           201             354 :     if (node->numCols > 0)
                                202                 :     {
                                203             144 :         rustate->tempContext =
                                204             144 :             AllocSetContextCreate(CurrentMemoryContext,
                                205                 :                                   "RecursiveUnion",
                                206                 :                                   ALLOCSET_DEFAULT_SIZES);
                                207             144 :         rustate->tableContext =
                                208             144 :             AllocSetContextCreate(CurrentMemoryContext,
                                209                 :                                   "RecursiveUnion hash table",
                                210                 :                                   ALLOCSET_DEFAULT_SIZES);
                                211                 :     }
                                212                 : 
                                213                 :     /*
                                214                 :      * Make the state structure available to descendant WorkTableScan nodes
                                215                 :      * via the Param slot reserved for it.
                                216                 :      */
 5300                           217             354 :     prmdata = &(estate->es_param_exec_vals[node->wtParam]);
                                218             354 :     Assert(prmdata->execPlan == NULL);
                                219             354 :     prmdata->value = PointerGetDatum(rustate);
                                220             354 :     prmdata->isnull = false;
                                221                 : 
                                222                 :     /*
                                223                 :      * Miscellaneous initialization
                                224                 :      *
                                225                 :      * RecursiveUnion plans don't have expression contexts because they never
                                226                 :      * call ExecQual or ExecProject.
                                227                 :      */
                                228             354 :     Assert(node->plan.qual == NIL);
                                229                 : 
                                230                 :     /*
                                231                 :      * RecursiveUnion nodes still have Result slots, which hold pointers to
                                232                 :      * tuples, so we have to initialize them.
                                233                 :      */
 1612 andres                    234             354 :     ExecInitResultTypeTL(&rustate->ps);
                                235                 : 
                                236                 :     /*
                                237                 :      * Initialize result tuple type.  (Note: we have to set up the result type
                                238                 :      * before initializing child nodes, because nodeWorktablescan.c expects it
                                239                 :      * to be valid.)
                                240                 :      */
 5300 tgl                       241             354 :     rustate->ps.ps_ProjInfo = NULL;
                                242                 : 
                                243                 :     /*
                                244                 :      * initialize child nodes
                                245                 :      */
                                246             354 :     outerPlanState(rustate) = ExecInitNode(outerPlan(node), estate, eflags);
                                247             354 :     innerPlanState(rustate) = ExecInitNode(innerPlan(node), estate, eflags);
                                248                 : 
                                249                 :     /*
                                250                 :      * If hashing, precompute fmgr lookup data for inner loop, and create the
                                251                 :      * hash table.
                                252                 :      */
 5297                           253             354 :     if (node->numCols > 0)
                                254                 :     {
                                255             144 :         execTuplesHashPrepare(node->numCols,
                                256             144 :                               node->dupOperators,
                                257                 :                               &rustate->eqfuncoids,
                                258                 :                               &rustate->hashfunctions);
                                259             144 :         build_hash_table(rustate);
                                260                 :     }
                                261                 : 
 5300                           262             354 :     return rustate;
                                263                 : }
                                264                 : 
                                265                 : /* ----------------------------------------------------------------
                                266                 :  *      ExecEndRecursiveUnion
                                267                 :  *
                                268                 :  *      frees any storage allocated through C routines.
                                269                 :  * ----------------------------------------------------------------
                                270                 :  */
                                271                 : void
                                272             354 : ExecEndRecursiveUnion(RecursiveUnionState *node)
                                273                 : {
                                274                 :     /* Release tuplestores */
                                275             354 :     tuplestore_end(node->working_table);
                                276             354 :     tuplestore_end(node->intermediate_table);
                                277                 : 
                                278                 :     /* free subsidiary stuff including hashtable */
 5297                           279             354 :     if (node->tempContext)
                                280             144 :         MemoryContextDelete(node->tempContext);
                                281             354 :     if (node->tableContext)
                                282             144 :         MemoryContextDelete(node->tableContext);
                                283                 : 
                                284                 :     /*
                                285                 :      * close down subplans
                                286                 :      */
 5300                           287             354 :     ExecEndNode(outerPlanState(node));
                                288             354 :     ExecEndNode(innerPlanState(node));
                                289             354 : }
                                290                 : 
                                291                 : /* ----------------------------------------------------------------
                                292                 :  *      ExecReScanRecursiveUnion
                                293                 :  *
                                294                 :  *      Rescans the relation.
                                295                 :  * ----------------------------------------------------------------
                                296                 :  */
                                297                 : void
 4654                           298               6 : ExecReScanRecursiveUnion(RecursiveUnionState *node)
                                299                 : {
 5300                           300               6 :     PlanState  *outerPlan = outerPlanState(node);
                                301               6 :     PlanState  *innerPlan = innerPlanState(node);
                                302               6 :     RecursiveUnion *plan = (RecursiveUnion *) node->ps.plan;
                                303                 : 
                                304                 :     /*
                                305                 :      * Set recursive term's chgParam to tell it that we'll modify the working
                                306                 :      * table and therefore it has to rescan.
                                307                 :      */
                                308               6 :     innerPlan->chgParam = bms_add_member(innerPlan->chgParam, plan->wtParam);
                                309                 : 
                                310                 :     /*
                                311                 :      * if chgParam of subnode is not null then plan will be re-scanned by
                                312                 :      * first ExecProcNode.  Because of above, we only have to do this to the
                                313                 :      * non-recursive term.
                                314                 :      */
                                315               6 :     if (outerPlan->chgParam == NULL)
 4654 tgl                       316 UBC           0 :         ExecReScan(outerPlan);
                                317                 : 
                                318                 :     /* Release any hashtable storage */
 5297 tgl                       319 CBC           6 :     if (node->tableContext)
                                320               6 :         MemoryContextResetAndDeleteChildren(node->tableContext);
                                321                 : 
                                322                 :     /* Empty hashtable if needed */
                                323               6 :     if (plan->numCols > 0)
 1520 andres                    324               6 :         ResetTupleHashTable(node->hashtable);
                                325                 : 
                                326                 :     /* reset processing state */
 5300 tgl                       327               6 :     node->recursing = false;
                                328               6 :     node->intermediate_empty = true;
                                329               6 :     tuplestore_clear(node->working_table);
                                330               6 :     tuplestore_clear(node->intermediate_table);
                                331               6 : }
        

Generated by: LCOV version v1.16-55-g56c0a2a