PostgreSQL Source Code git master
Loading...
Searching...
No Matches
nodeSetOp.c File Reference
#include "postgres.h"
#include "access/htup_details.h"
#include "executor/executor.h"
#include "executor/nodeSetOp.h"
#include "miscadmin.h"
#include "utils/memutils.h"
#include "utils/sortsupport.h"
Include dependency graph for nodeSetOp.c:

Go to the source code of this file.

Data Structures

struct  SetOpStatePerGroupData
 

Typedefs

typedef struct SetOpStatePerGroupData SetOpStatePerGroupData
 
typedef SetOpStatePerGroupDataSetOpStatePerGroup
 

Functions

static TupleTableSlotsetop_retrieve_sorted (SetOpState *setopstate)
 
static void setop_load_group (SetOpStatePerInput *input, PlanState *inputPlan, SetOpState *setopstate)
 
static int setop_compare_slots (TupleTableSlot *s1, TupleTableSlot *s2, SetOpState *setopstate)
 
static void setop_fill_hash_table (SetOpState *setopstate)
 
static TupleTableSlotsetop_retrieve_hash_table (SetOpState *setopstate)
 
static void build_hash_table (SetOpState *setopstate)
 
Size EstimateSetOpHashTableSpace (double nentries, Size tupleWidth)
 
static void set_output_count (SetOpState *setopstate, SetOpStatePerGroup pergroup)
 
static TupleTableSlotExecSetOp (PlanState *pstate)
 
SetOpStateExecInitSetOp (SetOp *node, EState *estate, int eflags)
 
void ExecEndSetOp (SetOpState *node)
 
void ExecReScanSetOp (SetOpState *node)
 

Typedef Documentation

◆ SetOpStatePerGroup

◆ SetOpStatePerGroupData

Function Documentation

◆ build_hash_table()

static void build_hash_table ( SetOpState setopstate)
static

Definition at line 85 of file nodeSetOp.c.

86{
87 SetOp *node = (SetOp *) setopstate->ps.plan;
88 ExprContext *econtext = setopstate->ps.ps_ExprContext;
90
91 Assert(node->strategy == SETOP_HASHED);
92
93 /*
94 * If both child plans deliver the same fixed tuple slot type, we can tell
95 * BuildTupleHashTable to expect that slot type as input. Otherwise,
96 * we'll pass NULL denoting that any slot type is possible.
97 */
99 desc,
101 node->numCols,
102 node->cmpColIdx,
103 setopstate->eqfuncoids,
104 setopstate->hashfunctions,
105 node->cmpCollations,
106 node->numGroups,
108 setopstate->ps.state->es_query_cxt,
109 setopstate->tuplesContext,
110 econtext->ecxt_per_tuple_memory,
111 false);
112}
#define Assert(condition)
Definition c.h:945
TupleHashTable BuildTupleHashTable(PlanState *parent, TupleDesc inputDesc, const TupleTableSlotOps *inputOps, int numCols, AttrNumber *keyColIdx, const Oid *eqfuncoids, FmgrInfo *hashfunctions, Oid *collations, double nelements, Size additionalsize, MemoryContext metacxt, MemoryContext tuplescxt, MemoryContext tempcxt, bool use_variable_hash_iv)
TupleDesc ExecGetResultType(PlanState *planstate)
Definition execUtils.c:500
const TupleTableSlotOps * ExecGetCommonChildSlotOps(PlanState *ps)
Definition execUtils.c:568
#define outerPlanState(node)
Definition execnodes.h:1273
@ SETOP_HASHED
Definition nodes.h:417
static int fb(int x)
MemoryContext ecxt_per_tuple_memory
Definition execnodes.h:292
Plan plan
Definition plannodes.h:1444

References Assert, BuildTupleHashTable(), ExprContext::ecxt_per_tuple_memory, ExecGetCommonChildSlotOps(), ExecGetResultType(), fb(), SetOp::numCols, SetOp::numGroups, outerPlanState, SetOp::plan, SETOP_HASHED, and SetOp::strategy.

Referenced by ExecInitSetOp().

◆ EstimateSetOpHashTableSpace()

Size EstimateSetOpHashTableSpace ( double  nentries,
Size  tupleWidth 
)

Definition at line 116 of file nodeSetOp.c.

117{
118 return EstimateTupleHashTableSpace(nentries,
120 sizeof(SetOpStatePerGroupData));
121}
Size EstimateTupleHashTableSpace(double nentries, Size tupleWidth, Size additionalsize)

References EstimateTupleHashTableSpace(), and fb().

Referenced by create_setop_path().

◆ ExecEndSetOp()

void ExecEndSetOp ( SetOpState node)

Definition at line 692 of file nodeSetOp.c.

693{
694 /* free subsidiary stuff including hashtable data */
695 if (node->tuplesContext)
697
700}
void ExecEndNode(PlanState *node)
#define innerPlanState(node)
Definition execnodes.h:1272
void MemoryContextDelete(MemoryContext context)
Definition mcxt.c:472
MemoryContext tuplesContext
Definition execnodes.h:2726

References ExecEndNode(), innerPlanState, MemoryContextDelete(), outerPlanState, and SetOpState::tuplesContext.

Referenced by ExecEndNode().

◆ ExecInitSetOp()

SetOpState * ExecInitSetOp ( SetOp node,
EState estate,
int  eflags 
)

Definition at line 573 of file nodeSetOp.c.

574{
576
577 /* check for unsupported flags */
579
580 /*
581 * create state structure
582 */
584 setopstate->ps.plan = (Plan *) node;
585 setopstate->ps.state = estate;
586 setopstate->ps.ExecProcNode = ExecSetOp;
587
588 setopstate->setop_done = false;
589 setopstate->numOutput = 0;
590 setopstate->numCols = node->numCols;
591 setopstate->need_init = true;
592
593 /*
594 * create expression context
595 */
596 ExecAssignExprContext(estate, &setopstate->ps);
597
598 /*
599 * If hashing, we also need a longer-lived context to store the hash
600 * table. The table can't just be kept in the per-query context because
601 * we want to be able to throw it away in ExecReScanSetOp. We can use a
602 * BumpContext to save storage, because we will have no need to delete
603 * individual table entries.
604 */
605 if (node->strategy == SETOP_HASHED)
606 setopstate->tuplesContext =
608 "SetOp hashed tuples",
610
611 /*
612 * initialize child nodes
613 *
614 * If we are hashing then the child plans do not need to handle REWIND
615 * efficiently; see ExecReScanSetOp.
616 */
617 if (node->strategy == SETOP_HASHED)
618 eflags &= ~EXEC_FLAG_REWIND;
619 outerPlanState(setopstate) = ExecInitNode(outerPlan(node), estate, eflags);
620 innerPlanState(setopstate) = ExecInitNode(innerPlan(node), estate, eflags);
621
622 /*
623 * Initialize locally-allocated slots. In hashed mode, we just need a
624 * result slot. In sorted mode, we need one first-tuple-of-group slot for
625 * each input; we use the result slot for the left input's slot and create
626 * another for the right input. (Note: the nextTupleSlot slots are not
627 * ours, but just point to the last slot returned by the input plan node.)
628 */
630 if (node->strategy != SETOP_HASHED)
631 {
632 setopstate->leftInput.firstTupleSlot =
633 setopstate->ps.ps_ResultTupleSlot;
634 setopstate->rightInput.firstTupleSlot =
636 setopstate->ps.ps_ResultTupleDesc,
638 }
639
640 /* Setop nodes do no projections. */
641 setopstate->ps.ps_ProjInfo = NULL;
642
643 /*
644 * Precompute fmgr lookup data for inner loop. We need equality and
645 * hashing functions to do it by hashing, while for sorting we need
646 * SortSupport data.
647 */
648 if (node->strategy == SETOP_HASHED)
650 node->cmpOperators,
651 &setopstate->eqfuncoids,
652 &setopstate->hashfunctions);
653 else
654 {
655 int nkeys = node->numCols;
656
657 setopstate->sortKeys = (SortSupport)
658 palloc0(nkeys * sizeof(SortSupportData));
659 for (int i = 0; i < nkeys; i++)
660 {
661 SortSupport sortKey = setopstate->sortKeys + i;
662
664 sortKey->ssup_collation = node->cmpCollations[i];
665 sortKey->ssup_nulls_first = node->cmpNullsFirst[i];
666 sortKey->ssup_attno = node->cmpColIdx[i];
667 /* abbreviated key conversion is not useful here */
668 sortKey->abbreviate = false;
669
670 PrepareSortSupportFromOrderingOp(node->cmpOperators[i], sortKey);
671 }
672 }
673
674 /* Create a hash table if needed */
675 if (node->strategy == SETOP_HASHED)
676 {
678 setopstate->table_filled = false;
679 }
680
681 return setopstate;
682}
MemoryContext BumpContextCreate(MemoryContext parent, const char *name, Size minContextSize, Size initBlockSize, Size maxBlockSize)
Definition bump.c:133
void execTuplesHashPrepare(int numCols, const Oid *eqOperators, Oid **eqFuncOids, FmgrInfo **hashFunctions)
PlanState * ExecInitNode(Plan *node, EState *estate, int eflags)
TupleTableSlot * ExecInitExtraTupleSlot(EState *estate, TupleDesc tupledesc, const TupleTableSlotOps *tts_ops)
void ExecInitResultTupleSlotTL(PlanState *planstate, const TupleTableSlotOps *tts_ops)
const TupleTableSlotOps TTSOpsMinimalTuple
Definition execTuples.c:86
void ExecAssignExprContext(EState *estate, PlanState *planstate)
Definition execUtils.c:490
struct SortSupportData * SortSupport
Definition execnodes.h:60
#define EXEC_FLAG_BACKWARD
Definition executor.h:70
#define EXEC_FLAG_MARK
Definition executor.h:71
int i
Definition isn.c:77
void * palloc0(Size size)
Definition mcxt.c:1417
MemoryContext CurrentMemoryContext
Definition mcxt.c:160
#define ALLOCSET_DEFAULT_SIZES
Definition memutils.h:160
static void build_hash_table(SetOpState *setopstate)
Definition nodeSetOp.c:85
static TupleTableSlot * ExecSetOp(PlanState *pstate)
Definition nodeSetOp.c:169
#define makeNode(_type_)
Definition nodes.h:161
#define innerPlan(node)
Definition plannodes.h:264
#define outerPlan(node)
Definition plannodes.h:265
void PrepareSortSupportFromOrderingOp(Oid orderingOp, SortSupport ssup)
SetOpStrategy strategy
Definition plannodes.h:1450
int numCols
Definition plannodes.h:1453
MemoryContext ssup_cxt
Definition sortsupport.h:66

References ALLOCSET_DEFAULT_SIZES, Assert, build_hash_table(), BumpContextCreate(), CurrentMemoryContext, EXEC_FLAG_BACKWARD, EXEC_FLAG_MARK, ExecAssignExprContext(), ExecInitExtraTupleSlot(), ExecInitNode(), ExecInitResultTupleSlotTL(), ExecSetOp(), execTuplesHashPrepare(), fb(), i, innerPlan, innerPlanState, makeNode, SetOp::numCols, outerPlan, outerPlanState, palloc0(), PrepareSortSupportFromOrderingOp(), SETOP_HASHED, SortSupportData::ssup_cxt, SetOp::strategy, and TTSOpsMinimalTuple.

Referenced by ExecInitNode().

◆ ExecReScanSetOp()

void ExecReScanSetOp ( SetOpState node)

Definition at line 704 of file nodeSetOp.c.

705{
708
710 node->setop_done = false;
711 node->numOutput = 0;
712
713 if (((SetOp *) node->ps.plan)->strategy == SETOP_HASHED)
714 {
715 /*
716 * In the hashed case, if we haven't yet built the hash table then we
717 * can just return; nothing done yet, so nothing to undo. If subnode's
718 * chgParam is not NULL then it will be re-scanned by ExecProcNode,
719 * else no reason to re-scan it at all.
720 */
721 if (!node->table_filled)
722 return;
723
724 /*
725 * If we do have the hash table and the subplans do not have any
726 * parameter changes, then we can just rescan the existing hash table;
727 * no need to build it again.
728 */
729 if (outerPlan->chgParam == NULL && innerPlan->chgParam == NULL)
730 {
732 return;
733 }
734
735 /* Else, we must rebuild the hashtable */
737 node->table_filled = false;
738 }
739 else
740 {
741 /* Need to re-read first input from each side */
742 node->need_init = true;
743 }
744
745 /*
746 * if chgParam of subnode is not null then plan will be re-scanned by
747 * first ExecProcNode.
748 */
749 if (outerPlan->chgParam == NULL)
751 if (innerPlan->chgParam == NULL)
753}
void ExecReScan(PlanState *node)
Definition execAmi.c:78
void ResetTupleHashTable(TupleHashTable hashtable)
#define ResetTupleHashIterator(htable, iter)
Definition execnodes.h:910
Plan * plan
Definition execnodes.h:1177
TupleTableSlot * ps_ResultTupleSlot
Definition execnodes.h:1215
bool need_init
Definition execnodes.h:2720
TupleHashIterator hashiter
Definition execnodes.h:2728
bool table_filled
Definition execnodes.h:2727
PlanState ps
Definition execnodes.h:2711
TupleHashTable hashtable
Definition execnodes.h:2725
int64 numOutput
Definition execnodes.h:2713
bool setop_done
Definition execnodes.h:2712
static TupleTableSlot * ExecClearTuple(TupleTableSlot *slot)
Definition tuptable.h:476

References ExecClearTuple(), ExecReScan(), fb(), SetOpState::hashiter, SetOpState::hashtable, innerPlan, innerPlanState, SetOpState::need_init, SetOpState::numOutput, outerPlan, outerPlanState, PlanState::plan, SetOpState::ps, PlanState::ps_ResultTupleSlot, ResetTupleHashIterator, ResetTupleHashTable(), SetOpState::setop_done, SETOP_HASHED, and SetOpState::table_filled.

Referenced by ExecReScan().

◆ ExecSetOp()

static TupleTableSlot * ExecSetOp ( PlanState pstate)
static

Definition at line 169 of file nodeSetOp.c.

170{
171 SetOpState *node = castNode(SetOpState, pstate);
172 SetOp *plannode = (SetOp *) node->ps.plan;
173 TupleTableSlot *resultTupleSlot = node->ps.ps_ResultTupleSlot;
174
176
177 /*
178 * If the previously-returned tuple needs to be returned more than once,
179 * keep returning it.
180 */
181 if (node->numOutput > 0)
182 {
183 node->numOutput--;
184 return resultTupleSlot;
185 }
186
187 /* Otherwise, we're done if we are out of groups */
188 if (node->setop_done)
189 return NULL;
190
191 /* Fetch the next tuple group according to the correct strategy */
192 if (plannode->strategy == SETOP_HASHED)
193 {
194 if (!node->table_filled)
196 return setop_retrieve_hash_table(node);
197 }
198 else
199 return setop_retrieve_sorted(node);
200}
#define CHECK_FOR_INTERRUPTS()
Definition miscadmin.h:123
static void setop_fill_hash_table(SetOpState *setopstate)
Definition nodeSetOp.c:416
static TupleTableSlot * setop_retrieve_hash_table(SetOpState *setopstate)
Definition nodeSetOp.c:513
static TupleTableSlot * setop_retrieve_sorted(SetOpState *setopstate)
Definition nodeSetOp.c:206
#define castNode(_type_, nodeptr)
Definition nodes.h:182

References castNode, CHECK_FOR_INTERRUPTS, fb(), SetOpState::numOutput, PlanState::plan, SetOpState::ps, PlanState::ps_ResultTupleSlot, SetOpState::setop_done, setop_fill_hash_table(), SETOP_HASHED, setop_retrieve_hash_table(), setop_retrieve_sorted(), and SetOpState::table_filled.

Referenced by ExecInitSetOp().

◆ set_output_count()

static void set_output_count ( SetOpState setopstate,
SetOpStatePerGroup  pergroup 
)
static

Definition at line 129 of file nodeSetOp.c.

130{
132
133 switch (plannode->cmd)
134 {
136 if (pergroup->numLeft > 0 && pergroup->numRight > 0)
137 setopstate->numOutput = 1;
138 else
139 setopstate->numOutput = 0;
140 break;
142 setopstate->numOutput =
143 (pergroup->numLeft < pergroup->numRight) ?
144 pergroup->numLeft : pergroup->numRight;
145 break;
146 case SETOPCMD_EXCEPT:
147 if (pergroup->numLeft > 0 && pergroup->numRight == 0)
148 setopstate->numOutput = 1;
149 else
150 setopstate->numOutput = 0;
151 break;
153 setopstate->numOutput =
154 (pergroup->numLeft < pergroup->numRight) ?
155 0 : (pergroup->numLeft - pergroup->numRight);
156 break;
157 default:
158 elog(ERROR, "unrecognized set op: %d", (int) plannode->cmd);
159 break;
160 }
161}
#define ERROR
Definition elog.h:39
#define elog(elevel,...)
Definition elog.h:226
@ SETOPCMD_EXCEPT
Definition nodes.h:410
@ SETOPCMD_EXCEPT_ALL
Definition nodes.h:411
@ SETOPCMD_INTERSECT_ALL
Definition nodes.h:409
@ SETOPCMD_INTERSECT
Definition nodes.h:408

References elog, ERROR, fb(), SetOp::plan, SETOPCMD_EXCEPT, SETOPCMD_EXCEPT_ALL, SETOPCMD_INTERSECT, and SETOPCMD_INTERSECT_ALL.

Referenced by setop_retrieve_hash_table(), and setop_retrieve_sorted().

◆ setop_compare_slots()

static int setop_compare_slots ( TupleTableSlot s1,
TupleTableSlot s2,
SetOpState setopstate 
)
static

Definition at line 387 of file nodeSetOp.c.

389{
390 /* We'll often need to fetch all the columns, so just do it */
393 for (int nkey = 0; nkey < setopstate->numCols; nkey++)
394 {
395 SortSupport sortKey = setopstate->sortKeys + nkey;
397 Datum datum1 = s1->tts_values[attno - 1],
398 datum2 = s2->tts_values[attno - 1];
399 bool isNull1 = s1->tts_isnull[attno - 1],
400 isNull2 = s2->tts_isnull[attno - 1];
401 int compare;
402
405 sortKey);
406 if (compare != 0)
407 return compare;
408 }
409 return 0;
410}
int16 AttrNumber
Definition attnum.h:21
static int compare(const void *arg1, const void *arg2)
Definition geqo_pool.c:144
uint64_t Datum
Definition postgres.h:70
char * s1
char * s2
static int ApplySortComparator(Datum datum1, bool isNull1, Datum datum2, bool isNull2, SortSupport ssup)
AttrNumber ssup_attno
Definition sortsupport.h:81
static void slot_getallattrs(TupleTableSlot *slot)
Definition tuptable.h:390

References ApplySortComparator(), compare(), fb(), s1, s2, slot_getallattrs(), and SortSupportData::ssup_attno.

Referenced by setop_load_group(), and setop_retrieve_sorted().

◆ setop_fill_hash_table()

static void setop_fill_hash_table ( SetOpState setopstate)
static

Definition at line 416 of file nodeSetOp.c.

417{
420 ExprContext *econtext = setopstate->ps.ps_ExprContext;
421 bool have_tuples = false;
422
423 /*
424 * get state info from node
425 */
428
429 /*
430 * Process each outer-plan tuple, and then fetch the next one, until we
431 * exhaust the outer plan.
432 */
433 for (;;)
434 {
436 TupleHashTable hashtable = setopstate->hashtable;
437 TupleHashEntryData *entry;
439 bool isnew;
440
442 if (TupIsNull(outerslot))
443 break;
444 have_tuples = true;
445
446 /* Find or build hashtable entry for this tuple's group */
447 entry = LookupTupleHashEntry(hashtable,
448 outerslot,
449 &isnew, NULL);
450
451 pergroup = TupleHashEntryGetAdditional(hashtable, entry);
452 /* If new tuple group, initialize counts to zero */
453 if (isnew)
454 {
455 pergroup->numLeft = 0;
456 pergroup->numRight = 0;
457 }
458
459 /* Advance the counts */
460 pergroup->numLeft++;
461
462 /* Must reset expression context after each hashtable lookup */
463 ResetExprContext(econtext);
464 }
465
466 /*
467 * If the outer relation is empty, then we will emit nothing, and we don't
468 * need to read the inner relation at all.
469 */
470 if (have_tuples)
471 {
472 /*
473 * Process each inner-plan tuple, and then fetch the next one, until
474 * we exhaust the inner plan.
475 */
476 for (;;)
477 {
479 TupleHashTable hashtable = setopstate->hashtable;
480 TupleHashEntryData *entry;
481
483 if (TupIsNull(innerslot))
484 break;
485
486 /* For tuples not seen previously, do not make hashtable entry */
487 entry = LookupTupleHashEntry(hashtable,
488 innerslot,
489 NULL, NULL);
490
491 /* Advance the counts if entry is already present */
492 if (entry)
493 {
495
496 pergroup->numRight++;
497 }
498
499 /* Must reset expression context after each hashtable lookup */
500 ResetExprContext(econtext);
501 }
502 }
503
504 setopstate->table_filled = true;
505 /* Initialize to walk the hash table */
506 ResetTupleHashIterator(setopstate->hashtable, &setopstate->hashiter);
507}
TupleHashEntry LookupTupleHashEntry(TupleHashTable hashtable, TupleTableSlot *slot, bool *isnew, uint32 *hash)
static void * TupleHashEntryGetAdditional(TupleHashTable hashtable, TupleHashEntry entry)
Definition executor.h:193
#define ResetExprContext(econtext)
Definition executor.h:654
static TupleTableSlot * ExecProcNode(PlanState *node)
Definition executor.h:315
#define TupIsNull(slot)
Definition tuptable.h:325

References ExecProcNode(), fb(), innerPlan, innerPlanState, LookupTupleHashEntry(), outerPlan, outerPlanState, ResetExprContext, ResetTupleHashIterator, TupIsNull, and TupleHashEntryGetAdditional().

Referenced by ExecSetOp().

◆ setop_load_group()

static void setop_load_group ( SetOpStatePerInput input,
PlanState inputPlan,
SetOpState setopstate 
)
static

Definition at line 340 of file nodeSetOp.c.

342{
343 input->needGroup = false;
344
345 /* If we've exhausted this child plan, report an empty group */
346 if (TupIsNull(input->nextTupleSlot))
347 {
348 ExecClearTuple(input->firstTupleSlot);
349 input->numTuples = 0;
350 return;
351 }
352
353 /* Make a local copy of the first tuple for comparisons */
355 input->firstTupleSlot,
356 true);
357 /* and count it */
358 input->numTuples = 1;
359
360 /* Scan till we find the end-of-group */
361 for (;;)
362 {
363 int cmpresult;
364
365 /* Get next input tuple, if there is one */
366 input->nextTupleSlot = ExecProcNode(inputPlan);
367 if (TupIsNull(input->nextTupleSlot))
368 break;
369
370 /* There is; does it belong to same group as firstTuple? */
371 cmpresult = setop_compare_slots(input->firstTupleSlot,
372 input->nextTupleSlot,
373 setopstate);
374 Assert(cmpresult <= 0); /* else input is mis-sorted */
375 if (cmpresult != 0)
376 break;
377
378 /* Still in same group, so count this tuple */
379 input->numTuples++;
380 }
381}
TupleTableSlot * ExecStoreMinimalTuple(MinimalTuple mtup, TupleTableSlot *slot, bool shouldFree)
FILE * input
static int setop_compare_slots(TupleTableSlot *s1, TupleTableSlot *s2, SetOpState *setopstate)
Definition nodeSetOp.c:387
static MinimalTuple ExecCopySlotMinimalTuple(TupleTableSlot *slot)
Definition tuptable.h:514

References Assert, ExecClearTuple(), ExecCopySlotMinimalTuple(), ExecProcNode(), ExecStoreMinimalTuple(), fb(), input, setop_compare_slots(), and TupIsNull.

Referenced by setop_retrieve_sorted().

◆ setop_retrieve_hash_table()

static TupleTableSlot * setop_retrieve_hash_table ( SetOpState setopstate)
static

Definition at line 513 of file nodeSetOp.c.

514{
515 TupleHashEntry entry;
517
518 /*
519 * get state info from node
520 */
521 resultTupleSlot = setopstate->ps.ps_ResultTupleSlot;
522
523 /*
524 * We loop retrieving groups until we find one we should return
525 */
526 while (!setopstate->setop_done)
527 {
528 TupleHashTable hashtable = setopstate->hashtable;
530
532
533 /*
534 * Find the next entry in the hash table
535 */
536 entry = ScanTupleHashTable(hashtable, &setopstate->hashiter);
537 if (entry == NULL)
538 {
539 /* No more entries in hashtable, so done */
540 setopstate->setop_done = true;
541 return NULL;
542 }
543
544 /*
545 * See if we should emit any copies of this tuple, and if so return
546 * the first copy.
547 */
548 pergroup = TupleHashEntryGetAdditional(hashtable, entry);
550
551 if (setopstate->numOutput > 0)
552 {
553 setopstate->numOutput--;
556 false);
557 }
558 }
559
560 /* No more groups */
562 return NULL;
563}
#define ScanTupleHashTable(htable, iter)
Definition execnodes.h:912
static MinimalTuple TupleHashEntryGetTuple(TupleHashEntry entry)
Definition executor.h:179
static void set_output_count(SetOpState *setopstate, SetOpStatePerGroup pergroup)
Definition nodeSetOp.c:129

References CHECK_FOR_INTERRUPTS, ExecClearTuple(), ExecStoreMinimalTuple(), fb(), ScanTupleHashTable, set_output_count(), TupleHashEntryGetAdditional(), and TupleHashEntryGetTuple().

Referenced by ExecSetOp().

◆ setop_retrieve_sorted()

static TupleTableSlot * setop_retrieve_sorted ( SetOpState setopstate)
static

Definition at line 206 of file nodeSetOp.c.

207{
211
212 /*
213 * get state info from node
214 */
217 resultTupleSlot = setopstate->ps.ps_ResultTupleSlot;
218
219 /*
220 * If first time through, establish the invariant that setop_load_group
221 * expects: each side's nextTupleSlot is the next output from the child
222 * plan, or empty if there is no more output from it.
223 */
224 if (setopstate->need_init)
225 {
226 setopstate->need_init = false;
227
228 setopstate->leftInput.nextTupleSlot = ExecProcNode(outerPlan);
229
230 /*
231 * If the outer relation is empty, then we will emit nothing, and we
232 * don't need to read the inner relation at all.
233 */
234 if (TupIsNull(setopstate->leftInput.nextTupleSlot))
235 {
236 setopstate->setop_done = true;
237 return NULL;
238 }
239
240 setopstate->rightInput.nextTupleSlot = ExecProcNode(innerPlan);
241
242 /* Set flags that we've not completed either side's group */
243 setopstate->leftInput.needGroup = true;
244 setopstate->rightInput.needGroup = true;
245 }
246
247 /*
248 * We loop retrieving groups until we find one we should return
249 */
250 while (!setopstate->setop_done)
251 {
252 int cmpresult;
254
255 /*
256 * Fetch the rest of the current outer group, if we didn't already.
257 */
258 if (setopstate->leftInput.needGroup)
260
261 /*
262 * If no more outer groups, we're done, and don't need to look at any
263 * more of the inner relation.
264 */
265 if (setopstate->leftInput.numTuples == 0)
266 {
267 setopstate->setop_done = true;
268 break;
269 }
270
271 /*
272 * Fetch the rest of the current inner group, if we didn't already.
273 */
274 if (setopstate->rightInput.needGroup)
276
277 /*
278 * Determine whether we have matching groups on both sides (this is
279 * basically like the core logic of a merge join).
280 */
281 if (setopstate->rightInput.numTuples == 0)
282 cmpresult = -1; /* as though left input is lesser */
283 else
284 cmpresult = setop_compare_slots(setopstate->leftInput.firstTupleSlot,
285 setopstate->rightInput.firstTupleSlot,
286 setopstate);
287
288 if (cmpresult < 0)
289 {
290 /* Left group is first, and has no right matches */
291 pergroup.numLeft = setopstate->leftInput.numTuples;
292 pergroup.numRight = 0;
293 /* We'll need another left group next time */
294 setopstate->leftInput.needGroup = true;
295 }
296 else if (cmpresult == 0)
297 {
298 /* We have matching groups */
299 pergroup.numLeft = setopstate->leftInput.numTuples;
300 pergroup.numRight = setopstate->rightInput.numTuples;
301 /* We'll need to read from both sides next time */
302 setopstate->leftInput.needGroup = true;
303 setopstate->rightInput.needGroup = true;
304 }
305 else
306 {
307 /* Right group has no left matches, so we can ignore it */
308 setopstate->rightInput.needGroup = true;
309 continue;
310 }
311
312 /*
313 * Done scanning these input tuple groups. See if we should emit any
314 * copies of result tuple, and if so return the first copy. (Note
315 * that the result tuple is the same as the left input's firstTuple
316 * slot.)
317 */
319
320 if (setopstate->numOutput > 0)
321 {
322 setopstate->numOutput--;
323 return resultTupleSlot;
324 }
325 }
326
327 /* No more groups */
329 return NULL;
330}
static void setop_load_group(SetOpStatePerInput *input, PlanState *inputPlan, SetOpState *setopstate)
Definition nodeSetOp.c:340

References ExecClearTuple(), ExecProcNode(), fb(), innerPlan, innerPlanState, outerPlan, outerPlanState, set_output_count(), setop_compare_slots(), setop_load_group(), and TupIsNull.

Referenced by ExecSetOp().