LCOV - code coverage report
Current view: top level - src/backend/executor - nodeSetOp.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 95.3 % 234 223
Test Date: 2026-03-01 00:15:48 Functions: 100.0 % 12 12
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /*-------------------------------------------------------------------------
       2              :  *
       3              :  * nodeSetOp.c
       4              :  *    Routines to handle INTERSECT and EXCEPT selection
       5              :  *
       6              :  * The input of a SetOp node consists of two relations (outer and inner)
       7              :  * with identical column sets.  In EXCEPT queries the outer relation is
       8              :  * always the left side, while in INTERSECT cases the planner tries to
       9              :  * make the outer relation be the smaller of the two inputs.
      10              :  *
      11              :  * In SETOP_SORTED mode, each input has been sorted according to all the
      12              :  * grouping columns.  The SetOp node essentially performs a merge join on
      13              :  * the grouping columns, except that it is only interested in counting how
      14              :  * many tuples from each input match.  Then it is a simple matter to emit
      15              :  * the output demanded by the SQL spec for INTERSECT, INTERSECT ALL, EXCEPT,
      16              :  * or EXCEPT ALL.
      17              :  *
      18              :  * In SETOP_HASHED mode, the inputs are delivered in no particular order.
      19              :  * We read the outer relation and build a hash table in memory with one entry
      20              :  * for each group of identical tuples, counting the number of tuples in the
      21              :  * group.  Then we read the inner relation and count the number of tuples
      22              :  * matching each outer group.  (We can disregard any tuples appearing only
      23              :  * in the inner relation, since they cannot result in any output.)  After
      24              :  * seeing all the input, we scan the hashtable and generate the correct
      25              :  * output using those counts.
      26              :  *
      27              :  * This node type is not used for UNION or UNION ALL, since those can be
      28              :  * implemented more cheaply (there's no need to count the number of
      29              :  * matching tuples).
      30              :  *
      31              :  * Note that SetOp does no qual checking nor projection.  The delivered
      32              :  * output tuples are just copies of the first-to-arrive tuple in each
      33              :  * input group.
      34              :  *
      35              :  *
      36              :  * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
      37              :  * Portions Copyright (c) 1994, Regents of the University of California
      38              :  *
      39              :  *
      40              :  * IDENTIFICATION
      41              :  *    src/backend/executor/nodeSetOp.c
      42              :  *
      43              :  *-------------------------------------------------------------------------
      44              :  */
      45              : 
      46              : #include "postgres.h"
      47              : 
      48              : #include "access/htup_details.h"
      49              : #include "executor/executor.h"
      50              : #include "executor/nodeSetOp.h"
      51              : #include "miscadmin.h"
      52              : #include "utils/memutils.h"
      53              : 
      54              : 
      55              : /*
      56              :  * SetOpStatePerGroupData - per-group working state
      57              :  *
      58              :  * In SETOP_SORTED mode, we need only one of these structs, and it's just a
      59              :  * local in setop_retrieve_sorted.  In SETOP_HASHED mode, the hash table
      60              :  * contains one of these for each tuple group.
      61              :  */
      62              : typedef struct SetOpStatePerGroupData
      63              : {
      64              :     int64       numLeft;        /* number of left-input dups in group */
      65              :     int64       numRight;       /* number of right-input dups in group */
      66              : } SetOpStatePerGroupData;
      67              : 
      68              : typedef SetOpStatePerGroupData *SetOpStatePerGroup;
      69              : 
      70              : 
      71              : static TupleTableSlot *setop_retrieve_sorted(SetOpState *setopstate);
      72              : static void setop_load_group(SetOpStatePerInput *input, PlanState *inputPlan,
      73              :                              SetOpState *setopstate);
      74              : static int  setop_compare_slots(TupleTableSlot *s1, TupleTableSlot *s2,
      75              :                                 SetOpState *setopstate);
      76              : static void setop_fill_hash_table(SetOpState *setopstate);
      77              : static TupleTableSlot *setop_retrieve_hash_table(SetOpState *setopstate);
      78              : 
      79              : 
      80              : /*
      81              :  * Initialize the hash table to empty.
      82              :  */
      83              : static void
      84          193 : build_hash_table(SetOpState *setopstate)
      85              : {
      86          193 :     SetOp      *node = (SetOp *) setopstate->ps.plan;
      87          193 :     ExprContext *econtext = setopstate->ps.ps_ExprContext;
      88          193 :     TupleDesc   desc = ExecGetResultType(outerPlanState(setopstate));
      89              : 
      90              :     Assert(node->strategy == SETOP_HASHED);
      91              : 
      92              :     /*
      93              :      * If both child plans deliver the same fixed tuple slot type, we can tell
      94              :      * BuildTupleHashTable to expect that slot type as input.  Otherwise,
      95              :      * we'll pass NULL denoting that any slot type is possible.
      96              :      */
      97          193 :     setopstate->hashtable = BuildTupleHashTable(&setopstate->ps,
      98              :                                                 desc,
      99              :                                                 ExecGetCommonChildSlotOps(&setopstate->ps),
     100              :                                                 node->numCols,
     101              :                                                 node->cmpColIdx,
     102          193 :                                                 setopstate->eqfuncoids,
     103              :                                                 setopstate->hashfunctions,
     104              :                                                 node->cmpCollations,
     105              :                                                 node->numGroups,
     106              :                                                 sizeof(SetOpStatePerGroupData),
     107          193 :                                                 setopstate->ps.state->es_query_cxt,
     108              :                                                 setopstate->tuplesContext,
     109              :                                                 econtext->ecxt_per_tuple_memory,
     110              :                                                 false);
     111          193 : }
     112              : 
     113              : /* Planner support routine to estimate space needed for hash table */
     114              : Size
     115          310 : EstimateSetOpHashTableSpace(double nentries, Size tupleWidth)
     116              : {
     117          310 :     return EstimateTupleHashTableSpace(nentries,
     118              :                                        tupleWidth,
     119              :                                        sizeof(SetOpStatePerGroupData));
     120              : }
     121              : 
     122              : /*
     123              :  * We've completed processing a tuple group.  Decide how many copies (if any)
     124              :  * of its representative row to emit, and store the count into numOutput.
     125              :  * This logic is straight from the SQL92 specification.
     126              :  */
     127              : static void
     128       221586 : set_output_count(SetOpState *setopstate, SetOpStatePerGroup pergroup)
     129              : {
     130       221586 :     SetOp      *plannode = (SetOp *) setopstate->ps.plan;
     131              : 
     132       221586 :     switch (plannode->cmd)
     133              :     {
     134        30180 :         case SETOPCMD_INTERSECT:
     135        30180 :             if (pergroup->numLeft > 0 && pergroup->numRight > 0)
     136        30114 :                 setopstate->numOutput = 1;
     137              :             else
     138           66 :                 setopstate->numOutput = 0;
     139        30180 :             break;
     140           18 :         case SETOPCMD_INTERSECT_ALL:
     141           18 :             setopstate->numOutput =
     142           18 :                 (pergroup->numLeft < pergroup->numRight) ?
     143           18 :                 pergroup->numLeft : pergroup->numRight;
     144           18 :             break;
     145       185346 :         case SETOPCMD_EXCEPT:
     146       185346 :             if (pergroup->numLeft > 0 && pergroup->numRight == 0)
     147          711 :                 setopstate->numOutput = 1;
     148              :             else
     149       184635 :                 setopstate->numOutput = 0;
     150       185346 :             break;
     151         6042 :         case SETOPCMD_EXCEPT_ALL:
     152         6042 :             setopstate->numOutput =
     153         6042 :                 (pergroup->numLeft < pergroup->numRight) ?
     154         6042 :                 0 : (pergroup->numLeft - pergroup->numRight);
     155         6042 :             break;
     156            0 :         default:
     157            0 :             elog(ERROR, "unrecognized set op: %d", (int) plannode->cmd);
     158              :             break;
     159              :     }
     160       221586 : }
     161              : 
     162              : 
     163              : /* ----------------------------------------------------------------
     164              :  *      ExecSetOp
     165              :  * ----------------------------------------------------------------
     166              :  */
     167              : static TupleTableSlot *         /* return: a tuple or NULL */
     168        31771 : ExecSetOp(PlanState *pstate)
     169              : {
     170        31771 :     SetOpState *node = castNode(SetOpState, pstate);
     171        31771 :     SetOp      *plannode = (SetOp *) node->ps.plan;
     172        31771 :     TupleTableSlot *resultTupleSlot = node->ps.ps_ResultTupleSlot;
     173              : 
     174        31771 :     CHECK_FOR_INTERRUPTS();
     175              : 
     176              :     /*
     177              :      * If the previously-returned tuple needs to be returned more than once,
     178              :      * keep returning it.
     179              :      */
     180        31771 :     if (node->numOutput > 0)
     181              :     {
     182           24 :         node->numOutput--;
     183           24 :         return resultTupleSlot;
     184              :     }
     185              : 
     186              :     /* Otherwise, we're done if we are out of groups */
     187        31747 :     if (node->setop_done)
     188            0 :         return NULL;
     189              : 
     190              :     /* Fetch the next tuple group according to the correct strategy */
     191        31747 :     if (plannode->strategy == SETOP_HASHED)
     192              :     {
     193        15940 :         if (!node->table_filled)
     194          466 :             setop_fill_hash_table(node);
     195        15940 :         return setop_retrieve_hash_table(node);
     196              :     }
     197              :     else
     198        15807 :         return setop_retrieve_sorted(node);
     199              : }
     200              : 
     201              : /*
     202              :  * ExecSetOp for non-hashed case
     203              :  */
     204              : static TupleTableSlot *
     205        15807 : setop_retrieve_sorted(SetOpState *setopstate)
     206              : {
     207              :     PlanState  *outerPlan;
     208              :     PlanState  *innerPlan;
     209              :     TupleTableSlot *resultTupleSlot;
     210              : 
     211              :     /*
     212              :      * get state info from node
     213              :      */
     214        15807 :     outerPlan = outerPlanState(setopstate);
     215        15807 :     innerPlan = innerPlanState(setopstate);
     216        15807 :     resultTupleSlot = setopstate->ps.ps_ResultTupleSlot;
     217              : 
     218              :     /*
     219              :      * If first time through, establish the invariant that setop_load_group
     220              :      * expects: each side's nextTupleSlot is the next output from the child
     221              :      * plan, or empty if there is no more output from it.
     222              :      */
     223        15807 :     if (setopstate->need_init)
     224              :     {
     225          411 :         setopstate->need_init = false;
     226              : 
     227          411 :         setopstate->leftInput.nextTupleSlot = ExecProcNode(outerPlan);
     228              : 
     229              :         /*
     230              :          * If the outer relation is empty, then we will emit nothing, and we
     231              :          * don't need to read the inner relation at all.
     232              :          */
     233          411 :         if (TupIsNull(setopstate->leftInput.nextTupleSlot))
     234              :         {
     235            0 :             setopstate->setop_done = true;
     236            0 :             return NULL;
     237              :         }
     238              : 
     239          411 :         setopstate->rightInput.nextTupleSlot = ExecProcNode(innerPlan);
     240              : 
     241              :         /* Set flags that we've not completed either side's group */
     242          411 :         setopstate->leftInput.needGroup = true;
     243          411 :         setopstate->rightInput.needGroup = true;
     244              :     }
     245              : 
     246              :     /*
     247              :      * We loop retrieving groups until we find one we should return
     248              :      */
     249        46053 :     while (!setopstate->setop_done)
     250              :     {
     251              :         int         cmpresult;
     252              :         SetOpStatePerGroupData pergroup;
     253              : 
     254              :         /*
     255              :          * Fetch the rest of the current outer group, if we didn't already.
     256              :          */
     257        46053 :         if (setopstate->leftInput.needGroup)
     258        45900 :             setop_load_group(&setopstate->leftInput, outerPlan, setopstate);
     259              : 
     260              :         /*
     261              :          * If no more outer groups, we're done, and don't need to look at any
     262              :          * more of the inner relation.
     263              :          */
     264        46053 :         if (setopstate->leftInput.numTuples == 0)
     265              :         {
     266          411 :             setopstate->setop_done = true;
     267          411 :             break;
     268              :         }
     269              : 
     270              :         /*
     271              :          * Fetch the rest of the current inner group, if we didn't already.
     272              :          */
     273        45642 :         if (setopstate->rightInput.needGroup)
     274        45603 :             setop_load_group(&setopstate->rightInput, innerPlan, setopstate);
     275              : 
     276              :         /*
     277              :          * Determine whether we have matching groups on both sides (this is
     278              :          * basically like the core logic of a merge join).
     279              :          */
     280        45642 :         if (setopstate->rightInput.numTuples == 0)
     281          129 :             cmpresult = -1;     /* as though left input is lesser */
     282              :         else
     283        45513 :             cmpresult = setop_compare_slots(setopstate->leftInput.firstTupleSlot,
     284              :                                             setopstate->rightInput.firstTupleSlot,
     285              :                                             setopstate);
     286              : 
     287        45642 :         if (cmpresult < 0)
     288              :         {
     289              :             /* Left group is first, and has no right matches */
     290          366 :             pergroup.numLeft = setopstate->leftInput.numTuples;
     291          366 :             pergroup.numRight = 0;
     292              :             /* We'll need another left group next time */
     293          366 :             setopstate->leftInput.needGroup = true;
     294              :         }
     295        45276 :         else if (cmpresult == 0)
     296              :         {
     297              :             /* We have matching groups */
     298        45123 :             pergroup.numLeft = setopstate->leftInput.numTuples;
     299        45123 :             pergroup.numRight = setopstate->rightInput.numTuples;
     300              :             /* We'll need to read from both sides next time */
     301        45123 :             setopstate->leftInput.needGroup = true;
     302        45123 :             setopstate->rightInput.needGroup = true;
     303              :         }
     304              :         else
     305              :         {
     306              :             /* Right group has no left matches, so we can ignore it */
     307          153 :             setopstate->rightInput.needGroup = true;
     308          153 :             continue;
     309              :         }
     310              : 
     311              :         /*
     312              :          * Done scanning these input tuple groups.  See if we should emit any
     313              :          * copies of result tuple, and if so return the first copy.  (Note
     314              :          * that the result tuple is the same as the left input's firstTuple
     315              :          * slot.)
     316              :          */
     317        45489 :         set_output_count(setopstate, &pergroup);
     318              : 
     319        45489 :         if (setopstate->numOutput > 0)
     320              :         {
     321        15396 :             setopstate->numOutput--;
     322        15396 :             return resultTupleSlot;
     323              :         }
     324              :     }
     325              : 
     326              :     /* No more groups */
     327          411 :     ExecClearTuple(resultTupleSlot);
     328          411 :     return NULL;
     329              : }
     330              : 
     331              : /*
     332              :  * Load next group of tuples from one child plan or the other.
     333              :  *
     334              :  * On entry, we've already read the first tuple of the next group
     335              :  * (if there is one) into input->nextTupleSlot.  This invariant
     336              :  * is maintained on exit.
     337              :  */
     338              : static void
     339        91503 : setop_load_group(SetOpStatePerInput *input, PlanState *inputPlan,
     340              :                  SetOpState *setopstate)
     341              : {
     342        91503 :     input->needGroup = false;
     343              : 
     344              :     /* If we've exhausted this child plan, report an empty group */
     345        91503 :     if (TupIsNull(input->nextTupleSlot))
     346              :     {
     347          537 :         ExecClearTuple(input->firstTupleSlot);
     348          537 :         input->numTuples = 0;
     349          537 :         return;
     350              :     }
     351              : 
     352              :     /* Make a local copy of the first tuple for comparisons */
     353        90966 :     ExecStoreMinimalTuple(ExecCopySlotMinimalTuple(input->nextTupleSlot),
     354              :                           input->firstTupleSlot,
     355              :                           true);
     356              :     /* and count it */
     357        90966 :     input->numTuples = 1;
     358              : 
     359              :     /* Scan till we find the end-of-group */
     360              :     for (;;)
     361        15165 :     {
     362              :         int         cmpresult;
     363              : 
     364              :         /* Get next input tuple, if there is one */
     365       106131 :         input->nextTupleSlot = ExecProcNode(inputPlan);
     366       106131 :         if (TupIsNull(input->nextTupleSlot))
     367              :             break;
     368              : 
     369              :         /* There is; does it belong to same group as firstTuple? */
     370       105312 :         cmpresult = setop_compare_slots(input->firstTupleSlot,
     371              :                                         input->nextTupleSlot,
     372              :                                         setopstate);
     373              :         Assert(cmpresult <= 0); /* else input is mis-sorted */
     374       105312 :         if (cmpresult != 0)
     375        90147 :             break;
     376              : 
     377              :         /* Still in same group, so count this tuple */
     378        15165 :         input->numTuples++;
     379              :     }
     380              : }
     381              : 
     382              : /*
     383              :  * Compare the tuples in the two given slots.
     384              :  */
     385              : static int
     386       150825 : setop_compare_slots(TupleTableSlot *s1, TupleTableSlot *s2,
     387              :                     SetOpState *setopstate)
     388              : {
     389              :     /* We'll often need to fetch all the columns, so just do it */
     390       150825 :     slot_getallattrs(s1);
     391       150825 :     slot_getallattrs(s2);
     392       212073 :     for (int nkey = 0; nkey < setopstate->numCols; nkey++)
     393              :     {
     394       151785 :         SortSupport sortKey = setopstate->sortKeys + nkey;
     395       151785 :         AttrNumber  attno = sortKey->ssup_attno;
     396       151785 :         Datum       datum1 = s1->tts_values[attno - 1],
     397       151785 :                     datum2 = s2->tts_values[attno - 1];
     398       151785 :         bool        isNull1 = s1->tts_isnull[attno - 1],
     399       151785 :                     isNull2 = s2->tts_isnull[attno - 1];
     400              :         int         compare;
     401              : 
     402       151785 :         compare = ApplySortComparator(datum1, isNull1,
     403              :                                       datum2, isNull2,
     404              :                                       sortKey);
     405       151785 :         if (compare != 0)
     406        90537 :             return compare;
     407              :     }
     408        60288 :     return 0;
     409              : }
     410              : 
     411              : /*
     412              :  * ExecSetOp for hashed case: phase 1, read inputs and build hash table
     413              :  */
     414              : static void
     415          466 : setop_fill_hash_table(SetOpState *setopstate)
     416              : {
     417              :     PlanState  *outerPlan;
     418              :     PlanState  *innerPlan;
     419          466 :     ExprContext *econtext = setopstate->ps.ps_ExprContext;
     420          466 :     bool        have_tuples = false;
     421              : 
     422              :     /*
     423              :      * get state info from node
     424              :      */
     425          466 :     outerPlan = outerPlanState(setopstate);
     426          466 :     innerPlan = innerPlanState(setopstate);
     427              : 
     428              :     /*
     429              :      * Process each outer-plan tuple, and then fetch the next one, until we
     430              :      * exhaust the outer plan.
     431              :      */
     432              :     for (;;)
     433       191207 :     {
     434              :         TupleTableSlot *outerslot;
     435       191673 :         TupleHashTable hashtable = setopstate->hashtable;
     436              :         TupleHashEntryData *entry;
     437              :         SetOpStatePerGroup pergroup;
     438              :         bool        isnew;
     439              : 
     440       191673 :         outerslot = ExecProcNode(outerPlan);
     441       191673 :         if (TupIsNull(outerslot))
     442              :             break;
     443       191207 :         have_tuples = true;
     444              : 
     445              :         /* Find or build hashtable entry for this tuple's group */
     446       191207 :         entry = LookupTupleHashEntry(hashtable,
     447              :                                      outerslot,
     448              :                                      &isnew, NULL);
     449              : 
     450       191207 :         pergroup = TupleHashEntryGetAdditional(hashtable, entry);
     451              :         /* If new tuple group, initialize counts to zero */
     452       191207 :         if (isnew)
     453              :         {
     454       176097 :             pergroup->numLeft = 0;
     455       176097 :             pergroup->numRight = 0;
     456              :         }
     457              : 
     458              :         /* Advance the counts */
     459       191207 :         pergroup->numLeft++;
     460              : 
     461              :         /* Must reset expression context after each hashtable lookup */
     462       191207 :         ResetExprContext(econtext);
     463              :     }
     464              : 
     465              :     /*
     466              :      * If the outer relation is empty, then we will emit nothing, and we don't
     467              :      * need to read the inner relation at all.
     468              :      */
     469          466 :     if (have_tuples)
     470              :     {
     471              :         /*
     472              :          * Process each inner-plan tuple, and then fetch the next one, until
     473              :          * we exhaust the inner plan.
     474              :          */
     475              :         for (;;)
     476       191171 :         {
     477              :             TupleTableSlot *innerslot;
     478       191637 :             TupleHashTable hashtable = setopstate->hashtable;
     479              :             TupleHashEntryData *entry;
     480              : 
     481       191637 :             innerslot = ExecProcNode(innerPlan);
     482       191637 :             if (TupIsNull(innerslot))
     483              :                 break;
     484              : 
     485              :             /* For tuples not seen previously, do not make hashtable entry */
     486       191171 :             entry = LookupTupleHashEntry(hashtable,
     487              :                                          innerslot,
     488              :                                          NULL, NULL);
     489              : 
     490              :             /* Advance the counts if entry is already present */
     491       191171 :             if (entry)
     492              :             {
     493       175769 :                 SetOpStatePerGroup pergroup = TupleHashEntryGetAdditional(hashtable, entry);
     494              : 
     495       175769 :                 pergroup->numRight++;
     496              :             }
     497              : 
     498              :             /* Must reset expression context after each hashtable lookup */
     499       191171 :             ResetExprContext(econtext);
     500              :         }
     501              :     }
     502              : 
     503          466 :     setopstate->table_filled = true;
     504              :     /* Initialize to walk the hash table */
     505          466 :     ResetTupleHashIterator(setopstate->hashtable, &setopstate->hashiter);
     506          466 : }
     507              : 
     508              : /*
     509              :  * ExecSetOp for hashed case: phase 2, retrieving groups from hash table
     510              :  */
     511              : static TupleTableSlot *
     512        15940 : setop_retrieve_hash_table(SetOpState *setopstate)
     513              : {
     514              :     TupleHashEntry entry;
     515              :     TupleTableSlot *resultTupleSlot;
     516              : 
     517              :     /*
     518              :      * get state info from node
     519              :      */
     520        15940 :     resultTupleSlot = setopstate->ps.ps_ResultTupleSlot;
     521              : 
     522              :     /*
     523              :      * We loop retrieving groups until we find one we should return
     524              :      */
     525       176563 :     while (!setopstate->setop_done)
     526              :     {
     527       176563 :         TupleHashTable hashtable = setopstate->hashtable;
     528              :         SetOpStatePerGroup pergroup;
     529              : 
     530       176563 :         CHECK_FOR_INTERRUPTS();
     531              : 
     532              :         /*
     533              :          * Find the next entry in the hash table
     534              :          */
     535       176563 :         entry = ScanTupleHashTable(hashtable, &setopstate->hashiter);
     536       176563 :         if (entry == NULL)
     537              :         {
     538              :             /* No more entries in hashtable, so done */
     539          466 :             setopstate->setop_done = true;
     540          466 :             return NULL;
     541              :         }
     542              : 
     543              :         /*
     544              :          * See if we should emit any copies of this tuple, and if so return
     545              :          * the first copy.
     546              :          */
     547       176097 :         pergroup = TupleHashEntryGetAdditional(hashtable, entry);
     548       176097 :         set_output_count(setopstate, pergroup);
     549              : 
     550       176097 :         if (setopstate->numOutput > 0)
     551              :         {
     552        15474 :             setopstate->numOutput--;
     553        15474 :             return ExecStoreMinimalTuple(TupleHashEntryGetTuple(entry),
     554              :                                          resultTupleSlot,
     555              :                                          false);
     556              :         }
     557              :     }
     558              : 
     559              :     /* No more groups */
     560            0 :     ExecClearTuple(resultTupleSlot);
     561            0 :     return NULL;
     562              : }
     563              : 
     564              : /* ----------------------------------------------------------------
     565              :  *      ExecInitSetOp
     566              :  *
     567              :  *      This initializes the setop node state structures and
     568              :  *      the node's subplan.
     569              :  * ----------------------------------------------------------------
     570              :  */
     571              : SetOpState *
     572          340 : ExecInitSetOp(SetOp *node, EState *estate, int eflags)
     573              : {
     574              :     SetOpState *setopstate;
     575              : 
     576              :     /* check for unsupported flags */
     577              :     Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)));
     578              : 
     579              :     /*
     580              :      * create state structure
     581              :      */
     582          340 :     setopstate = makeNode(SetOpState);
     583          340 :     setopstate->ps.plan = (Plan *) node;
     584          340 :     setopstate->ps.state = estate;
     585          340 :     setopstate->ps.ExecProcNode = ExecSetOp;
     586              : 
     587          340 :     setopstate->setop_done = false;
     588          340 :     setopstate->numOutput = 0;
     589          340 :     setopstate->numCols = node->numCols;
     590          340 :     setopstate->need_init = true;
     591              : 
     592              :     /*
     593              :      * create expression context
     594              :      */
     595          340 :     ExecAssignExprContext(estate, &setopstate->ps);
     596              : 
     597              :     /*
     598              :      * If hashing, we also need a longer-lived context to store the hash
     599              :      * table.  The table can't just be kept in the per-query context because
     600              :      * we want to be able to throw it away in ExecReScanSetOp.  We can use a
     601              :      * BumpContext to save storage, because we will have no need to delete
     602              :      * individual table entries.
     603              :      */
     604          340 :     if (node->strategy == SETOP_HASHED)
     605          193 :         setopstate->tuplesContext =
     606          193 :             BumpContextCreate(CurrentMemoryContext,
     607              :                               "SetOp hashed tuples",
     608              :                               ALLOCSET_DEFAULT_SIZES);
     609              : 
     610              :     /*
     611              :      * initialize child nodes
     612              :      *
     613              :      * If we are hashing then the child plans do not need to handle REWIND
     614              :      * efficiently; see ExecReScanSetOp.
     615              :      */
     616          340 :     if (node->strategy == SETOP_HASHED)
     617          193 :         eflags &= ~EXEC_FLAG_REWIND;
     618          340 :     outerPlanState(setopstate) = ExecInitNode(outerPlan(node), estate, eflags);
     619          340 :     innerPlanState(setopstate) = ExecInitNode(innerPlan(node), estate, eflags);
     620              : 
     621              :     /*
     622              :      * Initialize locally-allocated slots.  In hashed mode, we just need a
     623              :      * result slot.  In sorted mode, we need one first-tuple-of-group slot for
     624              :      * each input; we use the result slot for the left input's slot and create
     625              :      * another for the right input.  (Note: the nextTupleSlot slots are not
     626              :      * ours, but just point to the last slot returned by the input plan node.)
     627              :      */
     628          340 :     ExecInitResultTupleSlotTL(&setopstate->ps, &TTSOpsMinimalTuple);
     629          340 :     if (node->strategy != SETOP_HASHED)
     630              :     {
     631          147 :         setopstate->leftInput.firstTupleSlot =
     632          147 :             setopstate->ps.ps_ResultTupleSlot;
     633          147 :         setopstate->rightInput.firstTupleSlot =
     634          147 :             ExecInitExtraTupleSlot(estate,
     635              :                                    setopstate->ps.ps_ResultTupleDesc,
     636              :                                    &TTSOpsMinimalTuple);
     637              :     }
     638              : 
     639              :     /* Setop nodes do no projections. */
     640          340 :     setopstate->ps.ps_ProjInfo = NULL;
     641              : 
     642              :     /*
     643              :      * Precompute fmgr lookup data for inner loop.  We need equality and
     644              :      * hashing functions to do it by hashing, while for sorting we need
     645              :      * SortSupport data.
     646              :      */
     647          340 :     if (node->strategy == SETOP_HASHED)
     648          193 :         execTuplesHashPrepare(node->numCols,
     649          193 :                               node->cmpOperators,
     650              :                               &setopstate->eqfuncoids,
     651              :                               &setopstate->hashfunctions);
     652              :     else
     653              :     {
     654          147 :         int         nkeys = node->numCols;
     655              : 
     656          147 :         setopstate->sortKeys = (SortSupport)
     657          147 :             palloc0(nkeys * sizeof(SortSupportData));
     658          762 :         for (int i = 0; i < nkeys; i++)
     659              :         {
     660          615 :             SortSupport sortKey = setopstate->sortKeys + i;
     661              : 
     662          615 :             sortKey->ssup_cxt = CurrentMemoryContext;
     663          615 :             sortKey->ssup_collation = node->cmpCollations[i];
     664          615 :             sortKey->ssup_nulls_first = node->cmpNullsFirst[i];
     665          615 :             sortKey->ssup_attno = node->cmpColIdx[i];
     666              :             /* abbreviated key conversion is not useful here */
     667          615 :             sortKey->abbreviate = false;
     668              : 
     669          615 :             PrepareSortSupportFromOrderingOp(node->cmpOperators[i], sortKey);
     670              :         }
     671              :     }
     672              : 
     673              :     /* Create a hash table if needed */
     674          340 :     if (node->strategy == SETOP_HASHED)
     675              :     {
     676          193 :         build_hash_table(setopstate);
     677          193 :         setopstate->table_filled = false;
     678              :     }
     679              : 
     680          340 :     return setopstate;
     681              : }
     682              : 
     683              : /* ----------------------------------------------------------------
     684              :  *      ExecEndSetOp
     685              :  *
     686              :  *      This shuts down the subplans and frees resources allocated
     687              :  *      to this node.
     688              :  * ----------------------------------------------------------------
     689              :  */
     690              : void
     691          340 : ExecEndSetOp(SetOpState *node)
     692              : {
     693              :     /* free subsidiary stuff including hashtable data */
     694          340 :     if (node->tuplesContext)
     695          193 :         MemoryContextDelete(node->tuplesContext);
     696              : 
     697          340 :     ExecEndNode(outerPlanState(node));
     698          340 :     ExecEndNode(innerPlanState(node));
     699          340 : }
     700              : 
     701              : 
     702              : void
     703          600 : ExecReScanSetOp(SetOpState *node)
     704              : {
     705          600 :     PlanState  *outerPlan = outerPlanState(node);
     706          600 :     PlanState  *innerPlan = innerPlanState(node);
     707              : 
     708          600 :     ExecClearTuple(node->ps.ps_ResultTupleSlot);
     709          600 :     node->setop_done = false;
     710          600 :     node->numOutput = 0;
     711              : 
     712          600 :     if (((SetOp *) node->ps.plan)->strategy == SETOP_HASHED)
     713              :     {
     714              :         /*
     715              :          * In the hashed case, if we haven't yet built the hash table then we
     716              :          * can just return; nothing done yet, so nothing to undo. If subnode's
     717              :          * chgParam is not NULL then it will be re-scanned by ExecProcNode,
     718              :          * else no reason to re-scan it at all.
     719              :          */
     720          300 :         if (!node->table_filled)
     721            3 :             return;
     722              : 
     723              :         /*
     724              :          * If we do have the hash table and the subplans do not have any
     725              :          * parameter changes, then we can just rescan the existing hash table;
     726              :          * no need to build it again.
     727              :          */
     728          297 :         if (outerPlan->chgParam == NULL && innerPlan->chgParam == NULL)
     729              :         {
     730            0 :             ResetTupleHashIterator(node->hashtable, &node->hashiter);
     731            0 :             return;
     732              :         }
     733              : 
     734              :         /* Else, we must rebuild the hashtable */
     735          297 :         ResetTupleHashTable(node->hashtable);
     736          297 :         node->table_filled = false;
     737              :     }
     738              :     else
     739              :     {
     740              :         /* Need to re-read first input from each side */
     741          300 :         node->need_init = true;
     742              :     }
     743              : 
     744              :     /*
     745              :      * if chgParam of subnode is not null then plan will be re-scanned by
     746              :      * first ExecProcNode.
     747              :      */
     748          597 :     if (outerPlan->chgParam == NULL)
     749            0 :         ExecReScan(outerPlan);
     750          597 :     if (innerPlan->chgParam == NULL)
     751            0 :         ExecReScan(innerPlan);
     752              : }
        

Generated by: LCOV version 2.0-1