/src/postgres/src/backend/executor/nodeUnique.c
Line | Count | Source |
1 | | /*------------------------------------------------------------------------- |
2 | | * |
3 | | * nodeUnique.c |
4 | | * Routines to handle unique'ing of queries where appropriate |
5 | | * |
6 | | * Unique is a very simple node type that just filters out duplicate |
7 | | * tuples from a stream of sorted tuples from its subplan. It's essentially |
8 | | * a dumbed-down form of Group: the duplicate-removal functionality is |
9 | | * identical. However, Unique doesn't do projection nor qual checking, |
10 | | * so it's marginally more efficient for cases where neither is needed. |
11 | | * (It's debatable whether the savings justifies carrying two plan node |
12 | | * types, though.) |
13 | | * |
14 | | * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group |
15 | | * Portions Copyright (c) 1994, Regents of the University of California |
16 | | * |
17 | | * |
18 | | * IDENTIFICATION |
19 | | * src/backend/executor/nodeUnique.c |
20 | | * |
21 | | *------------------------------------------------------------------------- |
22 | | */ |
23 | | /* |
24 | | * INTERFACE ROUTINES |
25 | | * ExecUnique - generate a unique'd temporary relation |
26 | | * ExecInitUnique - initialize node and subnodes |
27 | | * ExecEndUnique - shutdown node and subnodes |
28 | | * |
29 | | * NOTES |
30 | | * Assumes tuples returned from subplan arrive in |
31 | | * sorted order. |
32 | | */ |
33 | | |
34 | | #include "postgres.h" |
35 | | |
36 | | #include "executor/executor.h" |
37 | | #include "executor/nodeUnique.h" |
38 | | #include "miscadmin.h" |
39 | | |
40 | | |
41 | | /* ---------------------------------------------------------------- |
42 | | * ExecUnique |
43 | | * ---------------------------------------------------------------- |
44 | | */ |
45 | | static TupleTableSlot * /* return: a tuple or NULL */ |
46 | | ExecUnique(PlanState *pstate) |
47 | 0 | { |
48 | 0 | UniqueState *node = castNode(UniqueState, pstate); |
49 | 0 | ExprContext *econtext = node->ps.ps_ExprContext; |
50 | 0 | TupleTableSlot *resultTupleSlot; |
51 | 0 | TupleTableSlot *slot; |
52 | 0 | PlanState *outerPlan; |
53 | |
|
54 | 0 | CHECK_FOR_INTERRUPTS(); |
55 | | |
56 | | /* |
57 | | * get information from the node |
58 | | */ |
59 | 0 | outerPlan = outerPlanState(node); |
60 | 0 | resultTupleSlot = node->ps.ps_ResultTupleSlot; |
61 | | |
62 | | /* |
63 | | * now loop, returning only non-duplicate tuples. We assume that the |
64 | | * tuples arrive in sorted order so we can detect duplicates easily. The |
65 | | * first tuple of each group is returned. |
66 | | */ |
67 | 0 | for (;;) |
68 | 0 | { |
69 | | /* |
70 | | * fetch a tuple from the outer subplan |
71 | | */ |
72 | 0 | slot = ExecProcNode(outerPlan); |
73 | 0 | if (TupIsNull(slot)) |
74 | 0 | { |
75 | | /* end of subplan, so we're done */ |
76 | 0 | ExecClearTuple(resultTupleSlot); |
77 | 0 | return NULL; |
78 | 0 | } |
79 | | |
80 | | /* |
81 | | * Always return the first tuple from the subplan. |
82 | | */ |
83 | 0 | if (TupIsNull(resultTupleSlot)) |
84 | 0 | break; |
85 | | |
86 | | /* |
87 | | * Else test if the new tuple and the previously returned tuple match. |
88 | | * If so then we loop back and fetch another new tuple from the |
89 | | * subplan. |
90 | | */ |
91 | 0 | econtext->ecxt_innertuple = slot; |
92 | 0 | econtext->ecxt_outertuple = resultTupleSlot; |
93 | 0 | if (!ExecQualAndReset(node->eqfunction, econtext)) |
94 | 0 | break; |
95 | 0 | } |
96 | | |
97 | | /* |
98 | | * We have a new tuple different from the previous saved tuple (if any). |
99 | | * Save it and return it. We must copy it because the source subplan |
100 | | * won't guarantee that this source tuple is still accessible after |
101 | | * fetching the next source tuple. |
102 | | */ |
103 | 0 | return ExecCopySlot(resultTupleSlot, slot); |
104 | 0 | } |
105 | | |
106 | | /* ---------------------------------------------------------------- |
107 | | * ExecInitUnique |
108 | | * |
109 | | * This initializes the unique node state structures and |
110 | | * the node's subplan. |
111 | | * ---------------------------------------------------------------- |
112 | | */ |
113 | | UniqueState * |
114 | | ExecInitUnique(Unique *node, EState *estate, int eflags) |
115 | 0 | { |
116 | 0 | UniqueState *uniquestate; |
117 | | |
118 | | /* check for unsupported flags */ |
119 | 0 | Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK))); |
120 | | |
121 | | /* |
122 | | * create state structure |
123 | | */ |
124 | 0 | uniquestate = makeNode(UniqueState); |
125 | 0 | uniquestate->ps.plan = (Plan *) node; |
126 | 0 | uniquestate->ps.state = estate; |
127 | 0 | uniquestate->ps.ExecProcNode = ExecUnique; |
128 | | |
129 | | /* |
130 | | * create expression context |
131 | | */ |
132 | 0 | ExecAssignExprContext(estate, &uniquestate->ps); |
133 | | |
134 | | /* |
135 | | * then initialize outer plan |
136 | | */ |
137 | 0 | outerPlanState(uniquestate) = ExecInitNode(outerPlan(node), estate, eflags); |
138 | | |
139 | | /* |
140 | | * Initialize result slot and type. Unique nodes do no projections, so |
141 | | * initialize projection info for this node appropriately. |
142 | | */ |
143 | 0 | ExecInitResultTupleSlotTL(&uniquestate->ps, &TTSOpsMinimalTuple); |
144 | 0 | uniquestate->ps.ps_ProjInfo = NULL; |
145 | | |
146 | | /* |
147 | | * Precompute fmgr lookup data for inner loop |
148 | | */ |
149 | 0 | uniquestate->eqfunction = |
150 | 0 | execTuplesMatchPrepare(ExecGetResultType(outerPlanState(uniquestate)), |
151 | 0 | node->numCols, |
152 | 0 | node->uniqColIdx, |
153 | 0 | node->uniqOperators, |
154 | 0 | node->uniqCollations, |
155 | 0 | &uniquestate->ps); |
156 | |
|
157 | 0 | return uniquestate; |
158 | 0 | } |
159 | | |
160 | | /* ---------------------------------------------------------------- |
161 | | * ExecEndUnique |
162 | | * |
163 | | * This shuts down the subplan and frees resources allocated |
164 | | * to this node. |
165 | | * ---------------------------------------------------------------- |
166 | | */ |
167 | | void |
168 | | ExecEndUnique(UniqueState *node) |
169 | 0 | { |
170 | 0 | ExecEndNode(outerPlanState(node)); |
171 | 0 | } |
172 | | |
173 | | |
174 | | void |
175 | | ExecReScanUnique(UniqueState *node) |
176 | 0 | { |
177 | 0 | PlanState *outerPlan = outerPlanState(node); |
178 | | |
179 | | /* must clear result tuple so first input tuple is returned */ |
180 | 0 | ExecClearTuple(node->ps.ps_ResultTupleSlot); |
181 | | |
182 | | /* |
183 | | * if chgParam of subnode is not null then plan will be re-scanned by |
184 | | * first ExecProcNode. |
185 | | */ |
186 | 0 | if (outerPlan->chgParam == NULL) |
187 | 0 | ExecReScan(outerPlan); |
188 | 0 | } |