Age Owner Branch data TLA Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * tlist.c
4 : : * Target list manipulation routines
5 : : *
6 : : * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
7 : : * Portions Copyright (c) 1994, Regents of the University of California
8 : : *
9 : : *
10 : : * IDENTIFICATION
11 : : * src/backend/optimizer/util/tlist.c
12 : : *
13 : : *-------------------------------------------------------------------------
14 : : */
15 : : #include "postgres.h"
16 : :
17 : : #include "nodes/makefuncs.h"
18 : : #include "nodes/nodeFuncs.h"
19 : : #include "optimizer/cost.h"
20 : : #include "optimizer/optimizer.h"
21 : : #include "optimizer/tlist.h"
22 : :
23 : :
24 : : /*
25 : : * Test if an expression node represents a SRF call. Beware multiple eval!
26 : : *
27 : : * Please note that this is only meant for use in split_pathtarget_at_srfs();
28 : : * if you use it anywhere else, your code is almost certainly wrong for SRFs
29 : : * nested within expressions. Use expression_returns_set() instead.
30 : : */
31 : : #define IS_SRF_CALL(node) \
32 : : ((IsA(node, FuncExpr) && ((FuncExpr *) (node))->funcretset) || \
33 : : (IsA(node, OpExpr) && ((OpExpr *) (node))->opretset))
34 : :
35 : : /*
36 : : * Data structures for split_pathtarget_at_srfs(). To preserve the identity
37 : : * of sortgroupref items even if they are textually equal(), what we track is
38 : : * not just bare expressions but expressions plus their sortgroupref indexes.
39 : : */
40 : : typedef struct
41 : : {
42 : : Node *expr; /* some subexpression of a PathTarget */
43 : : Index sortgroupref; /* its sortgroupref, or 0 if none */
44 : : } split_pathtarget_item;
45 : :
46 : : typedef struct
47 : : {
48 : : /* This is a List of bare expressions: */
49 : : List *input_target_exprs; /* exprs available from input */
50 : : /* These are Lists of Lists of split_pathtarget_items: */
51 : : List *level_srfs; /* SRF exprs to evaluate at each level */
52 : : List *level_input_vars; /* input vars needed at each level */
53 : : List *level_input_srfs; /* input SRFs needed at each level */
54 : : /* These are Lists of split_pathtarget_items: */
55 : : List *current_input_vars; /* vars needed in current subexpr */
56 : : List *current_input_srfs; /* SRFs needed in current subexpr */
57 : : /* Auxiliary data for current split_pathtarget_walker traversal: */
58 : : int current_depth; /* max SRF depth in current subexpr */
59 : : Index current_sgref; /* current subexpr's sortgroupref, or 0 */
60 : : } split_pathtarget_context;
61 : :
62 : : static bool split_pathtarget_walker(Node *node,
63 : : split_pathtarget_context *context);
64 : : static void add_sp_item_to_pathtarget(PathTarget *target,
65 : : split_pathtarget_item *item);
66 : : static void add_sp_items_to_pathtarget(PathTarget *target, List *items);
67 : :
68 : :
69 : : /*****************************************************************************
70 : : * Target list creation and searching utilities
71 : : *****************************************************************************/
72 : :
73 : : /*
74 : : * tlist_member
75 : : * Finds the (first) member of the given tlist whose expression is
76 : : * equal() to the given expression. Result is NULL if no such member.
77 : : */
78 : : TargetEntry *
2593 peter_e@gmx.net 79 :CBC 17779 : tlist_member(Expr *node, List *targetlist)
80 : : {
81 : : ListCell *temp;
82 : :
9002 tgl@sss.pgh.pa.us 83 [ + + + + : 78501 : foreach(temp, targetlist)
+ + ]
84 : : {
8768 bruce@momjian.us 85 : 64924 : TargetEntry *tlentry = (TargetEntry *) lfirst(temp);
86 : :
9002 tgl@sss.pgh.pa.us 87 [ + + ]: 64924 : if (equal(node, tlentry->expr))
88 : 4202 : return tlentry;
89 : : }
9357 bruce@momjian.us 90 : 13577 : return NULL;
91 : : }
92 : :
93 : : /*
94 : : * tlist_member_match_var
95 : : * Same as above, except that we match the provided Var on the basis
96 : : * of varno/varattno/varlevelsup/vartype only, rather than full equal().
97 : : *
98 : : * This is needed in some cases where we can't be sure of an exact typmod
99 : : * match. For safety, though, we insist on vartype match.
100 : : */
101 : : static TargetEntry *
3887 tgl@sss.pgh.pa.us 102 : 1060 : tlist_member_match_var(Var *var, List *targetlist)
103 : : {
104 : : ListCell *temp;
105 : :
106 [ + - + - : 3115 : foreach(temp, targetlist)
+ - ]
107 : : {
108 : 3115 : TargetEntry *tlentry = (TargetEntry *) lfirst(temp);
109 : 3115 : Var *tlvar = (Var *) tlentry->expr;
110 : :
111 [ + - - + ]: 3115 : if (!tlvar || !IsA(tlvar, Var))
3887 tgl@sss.pgh.pa.us 112 :UBC 0 : continue;
3887 tgl@sss.pgh.pa.us 113 [ + - ]:CBC 3115 : if (var->varno == tlvar->varno &&
114 [ + + ]: 3115 : var->varattno == tlvar->varattno &&
2960 115 [ + - ]: 1060 : var->varlevelsup == tlvar->varlevelsup &&
116 [ + - ]: 1060 : var->vartype == tlvar->vartype)
3887 117 : 1060 : return tlentry;
118 : : }
3887 tgl@sss.pgh.pa.us 119 :UBC 0 : return NULL;
120 : : }
121 : :
122 : : /*
123 : : * add_to_flat_tlist
124 : : * Add more items to a flattened tlist (if they're not already in it)
125 : : *
126 : : * 'tlist' is the flattened tlist
127 : : * 'exprs' is a list of expressions (usually, but not necessarily, Vars)
128 : : *
129 : : * Returns the extended tlist.
130 : : */
131 : : List *
5586 tgl@sss.pgh.pa.us 132 :CBC 820 : add_to_flat_tlist(List *tlist, List *exprs)
133 : : {
6948 134 : 820 : int next_resno = list_length(tlist) + 1;
135 : : ListCell *lc;
136 : :
5586 137 [ + + + + : 4493 : foreach(lc, exprs)
+ + ]
138 : : {
2593 peter_e@gmx.net 139 : 3673 : Expr *expr = (Expr *) lfirst(lc);
140 : :
5586 tgl@sss.pgh.pa.us 141 [ + + ]: 3673 : if (!tlist_member(expr, tlist))
142 : : {
143 : : TargetEntry *tle;
144 : :
2489 145 : 3651 : tle = makeTargetEntry(copyObject(expr), /* copy needed?? */
6948 146 : 3651 : next_resno++,
147 : : NULL,
148 : : false);
149 : 3651 : tlist = lappend(tlist, tle);
150 : : }
151 : : }
9003 152 : 820 : return tlist;
153 : : }
154 : :
155 : :
156 : : /*
157 : : * get_tlist_exprs
158 : : * Get just the expression subtrees of a tlist
159 : : *
160 : : * Resjunk columns are ignored unless includeJunk is true
161 : : */
162 : : List *
5729 163 : 587 : get_tlist_exprs(List *tlist, bool includeJunk)
164 : : {
165 : 587 : List *result = NIL;
166 : : ListCell *l;
167 : :
168 [ + + + + : 1830 : foreach(l, tlist)
+ + ]
169 : : {
170 : 1243 : TargetEntry *tle = (TargetEntry *) lfirst(l);
171 : :
172 [ - + - - ]: 1243 : if (tle->resjunk && !includeJunk)
5729 tgl@sss.pgh.pa.us 173 :UBC 0 : continue;
174 : :
5729 tgl@sss.pgh.pa.us 175 :CBC 1243 : result = lappend(result, tle->expr);
176 : : }
177 : 587 : return result;
178 : : }
179 : :
180 : :
181 : : /*
182 : : * count_nonjunk_tlist_entries
183 : : * What it says ...
184 : : */
185 : : int
3588 186 : 15098 : count_nonjunk_tlist_entries(List *tlist)
187 : : {
188 : 15098 : int len = 0;
189 : : ListCell *l;
190 : :
191 [ + + + + : 31213 : foreach(l, tlist)
+ + ]
192 : : {
193 : 16115 : TargetEntry *tle = (TargetEntry *) lfirst(l);
194 : :
195 [ + + ]: 16115 : if (!tle->resjunk)
196 : 15178 : len++;
197 : : }
198 : 15098 : return len;
199 : : }
200 : :
201 : :
202 : : /*
203 : : * tlist_same_exprs
204 : : * Check whether two target lists contain the same expressions
205 : : *
206 : : * Note: this function is used to decide whether it's safe to jam a new tlist
207 : : * into a non-projection-capable plan node. Obviously we can't do that unless
208 : : * the node's tlist shows it already returns the column values we want.
209 : : * However, we can ignore the TargetEntry attributes resname, ressortgroupref,
210 : : * resorigtbl, resorigcol, and resjunk, because those are only labelings that
211 : : * don't affect the row values computed by the node. (Moreover, if we didn't
212 : : * ignore them, we'd frequently fail to make the desired optimization, since
213 : : * the planner tends to not bother to make resname etc. valid in intermediate
214 : : * plan nodes.) Note that on success, the caller must still jam the desired
215 : : * tlist into the plan node, else it won't have the desired labeling fields.
216 : : */
217 : : bool
4049 218 : 5837 : tlist_same_exprs(List *tlist1, List *tlist2)
219 : : {
220 : : ListCell *lc1,
221 : : *lc2;
222 : :
223 [ + + ]: 5837 : if (list_length(tlist1) != list_length(tlist2))
224 : 399 : return false; /* not same length, so can't match */
225 : :
226 [ + - + + : 19890 : forboth(lc1, tlist1, lc2, tlist2)
+ - + + +
+ + - +
+ ]
227 : : {
228 : 14901 : TargetEntry *tle1 = (TargetEntry *) lfirst(lc1);
229 : 14901 : TargetEntry *tle2 = (TargetEntry *) lfirst(lc2);
230 : :
231 [ + + ]: 14901 : if (!equal(tle1->expr, tle2->expr))
232 : 449 : return false;
233 : : }
234 : :
235 : 4989 : return true;
236 : : }
237 : :
238 : :
239 : : /*
240 : : * Does tlist have same output datatypes as listed in colTypes?
241 : : *
242 : : * Resjunk columns are ignored if junkOK is true; otherwise presence of
243 : : * a resjunk column will always cause a 'false' result.
244 : : *
245 : : * Note: currently no callers care about comparing typmods.
246 : : */
247 : : bool
5729 248 : 4917 : tlist_same_datatypes(List *tlist, List *colTypes, bool junkOK)
249 : : {
250 : : ListCell *l;
251 : 4917 : ListCell *curColType = list_head(colTypes);
252 : :
253 [ + + + + : 17151 : foreach(l, tlist)
+ + ]
254 : : {
255 : 12557 : TargetEntry *tle = (TargetEntry *) lfirst(l);
256 : :
257 [ + + ]: 12557 : if (tle->resjunk)
258 : : {
259 [ + + ]: 304 : if (!junkOK)
260 : 323 : return false;
261 : : }
262 : : else
263 : : {
264 [ - + ]: 12253 : if (curColType == NULL)
5729 tgl@sss.pgh.pa.us 265 :UBC 0 : return false; /* tlist longer than colTypes */
5729 tgl@sss.pgh.pa.us 266 [ + + ]:CBC 12253 : if (exprType((Node *) tle->expr) != lfirst_oid(curColType))
267 : 278 : return false;
1735 268 : 11975 : curColType = lnext(colTypes, curColType);
269 : : }
270 : : }
5729 271 [ - + ]: 4594 : if (curColType != NULL)
5729 tgl@sss.pgh.pa.us 272 :UBC 0 : return false; /* tlist shorter than colTypes */
5729 tgl@sss.pgh.pa.us 273 :CBC 4594 : return true;
274 : : }
275 : :
276 : : /*
277 : : * Does tlist have same exposed collations as listed in colCollations?
278 : : *
279 : : * Identical logic to the above, but for collations.
280 : : */
281 : : bool
4747 282 : 2193 : tlist_same_collations(List *tlist, List *colCollations, bool junkOK)
283 : : {
284 : : ListCell *l;
285 : 2193 : ListCell *curColColl = list_head(colCollations);
286 : :
287 [ + + + + : 8587 : foreach(l, tlist)
+ + ]
288 : : {
289 : 6394 : TargetEntry *tle = (TargetEntry *) lfirst(l);
290 : :
291 [ + + ]: 6394 : if (tle->resjunk)
292 : : {
293 [ - + ]: 259 : if (!junkOK)
4747 tgl@sss.pgh.pa.us 294 :UBC 0 : return false;
295 : : }
296 : : else
297 : : {
4747 tgl@sss.pgh.pa.us 298 [ - + ]:CBC 6135 : if (curColColl == NULL)
4747 tgl@sss.pgh.pa.us 299 :UBC 0 : return false; /* tlist longer than colCollations */
4747 tgl@sss.pgh.pa.us 300 [ - + ]:CBC 6135 : if (exprCollation((Node *) tle->expr) != lfirst_oid(curColColl))
4747 tgl@sss.pgh.pa.us 301 :UBC 0 : return false;
1735 tgl@sss.pgh.pa.us 302 :CBC 6135 : curColColl = lnext(colCollations, curColColl);
303 : : }
304 : : }
4747 305 [ - + ]: 2193 : if (curColColl != NULL)
4747 tgl@sss.pgh.pa.us 306 :UBC 0 : return false; /* tlist shorter than colCollations */
4747 tgl@sss.pgh.pa.us 307 :CBC 2193 : return true;
308 : : }
309 : :
310 : : /*
311 : : * apply_tlist_labeling
312 : : * Apply the TargetEntry labeling attributes of src_tlist to dest_tlist
313 : : *
314 : : * This is useful for reattaching column names etc to a plan's final output
315 : : * targetlist.
316 : : */
317 : : void
2960 318 : 254336 : apply_tlist_labeling(List *dest_tlist, List *src_tlist)
319 : : {
320 : : ListCell *ld,
321 : : *ls;
322 : :
323 [ - + ]: 254336 : Assert(list_length(dest_tlist) == list_length(src_tlist));
324 [ + + + + : 870074 : forboth(ld, dest_tlist, ls, src_tlist)
+ + + + +
+ + - +
+ ]
325 : : {
326 : 615738 : TargetEntry *dest_tle = (TargetEntry *) lfirst(ld);
327 : 615738 : TargetEntry *src_tle = (TargetEntry *) lfirst(ls);
328 : :
329 [ - + ]: 615738 : Assert(dest_tle->resno == src_tle->resno);
330 : 615738 : dest_tle->resname = src_tle->resname;
331 : 615738 : dest_tle->ressortgroupref = src_tle->ressortgroupref;
332 : 615738 : dest_tle->resorigtbl = src_tle->resorigtbl;
333 : 615738 : dest_tle->resorigcol = src_tle->resorigcol;
334 : 615738 : dest_tle->resjunk = src_tle->resjunk;
335 : : }
336 : 254336 : }
337 : :
338 : :
339 : : /*
340 : : * get_sortgroupref_tle
341 : : * Find the targetlist entry matching the given SortGroupRef index,
342 : : * and return it.
343 : : */
344 : : TargetEntry *
6002 345 : 102985 : get_sortgroupref_tle(Index sortref, List *targetList)
346 : : {
347 : : ListCell *l;
348 : :
9104 JanWieck@Yahoo.com 349 [ + - + - : 241908 : foreach(l, targetList)
+ - ]
350 : : {
9015 tgl@sss.pgh.pa.us 351 : 241908 : TargetEntry *tle = (TargetEntry *) lfirst(l);
352 : :
6002 353 [ + + ]: 241908 : if (tle->ressortgroupref == sortref)
8844 354 : 102985 : return tle;
355 : : }
356 : :
7569 tgl@sss.pgh.pa.us 357 [ # # ]:UBC 0 : elog(ERROR, "ORDER/GROUP BY expression not found in targetlist");
358 : : return NULL; /* keep compiler quiet */
359 : : }
360 : :
361 : : /*
362 : : * get_sortgroupclause_tle
363 : : * Find the targetlist entry matching the given SortGroupClause
364 : : * by ressortgroupref, and return it.
365 : : */
366 : : TargetEntry *
5734 tgl@sss.pgh.pa.us 367 :CBC 102254 : get_sortgroupclause_tle(SortGroupClause *sgClause,
368 : : List *targetList)
369 : : {
370 : 102254 : return get_sortgroupref_tle(sgClause->tleSortGroupRef, targetList);
371 : : }
372 : :
373 : : /*
374 : : * get_sortgroupclause_expr
375 : : * Find the targetlist entry matching the given SortGroupClause
376 : : * by ressortgroupref, and return its expression.
377 : : */
378 : : Node *
379 : 83368 : get_sortgroupclause_expr(SortGroupClause *sgClause, List *targetList)
380 : : {
381 : 83368 : TargetEntry *tle = get_sortgroupclause_tle(sgClause, targetList);
382 : :
7794 383 : 83368 : return (Node *) tle->expr;
384 : : }
385 : :
386 : : /*
387 : : * get_sortgrouplist_exprs
388 : : * Given a list of SortGroupClauses, build a list
389 : : * of the referenced targetlist expressions.
390 : : */
391 : : List *
5734 392 : 6441 : get_sortgrouplist_exprs(List *sgClauses, List *targetList)
393 : : {
7559 bruce@momjian.us 394 : 6441 : List *result = NIL;
395 : : ListCell *l;
396 : :
5734 tgl@sss.pgh.pa.us 397 [ + + + + : 15244 : foreach(l, sgClauses)
+ + ]
398 : : {
399 : 8803 : SortGroupClause *sortcl = (SortGroupClause *) lfirst(l);
400 : : Node *sortexpr;
401 : :
7755 402 : 8803 : sortexpr = get_sortgroupclause_expr(sortcl, targetList);
403 : 8803 : result = lappend(result, sortexpr);
404 : : }
405 : 6441 : return result;
406 : : }
407 : :
408 : :
409 : : /*****************************************************************************
410 : : * Functions to extract data from a list of SortGroupClauses
411 : : *
412 : : * These don't really belong in tlist.c, but they are sort of related to the
413 : : * functions just above, and they don't seem to deserve their own file.
414 : : *****************************************************************************/
415 : :
416 : : /*
417 : : * get_sortgroupref_clause
418 : : * Find the SortGroupClause matching the given SortGroupRef index,
419 : : * and return it.
420 : : */
421 : : SortGroupClause *
3256 andres@anarazel.de 422 : 2627 : get_sortgroupref_clause(Index sortref, List *clauses)
423 : : {
424 : : ListCell *l;
425 : :
426 [ + - + - : 5172 : foreach(l, clauses)
+ - ]
427 : : {
428 : 5172 : SortGroupClause *cl = (SortGroupClause *) lfirst(l);
429 : :
430 [ + + ]: 5172 : if (cl->tleSortGroupRef == sortref)
431 : 2627 : return cl;
432 : : }
433 : :
3256 andres@anarazel.de 434 [ # # ]:UBC 0 : elog(ERROR, "ORDER/GROUP BY expression not found in list");
435 : : return NULL; /* keep compiler quiet */
436 : : }
437 : :
438 : : /*
439 : : * get_sortgroupref_clause_noerr
440 : : * As above, but return NULL rather than throwing an error if not found.
441 : : */
442 : : SortGroupClause *
2959 tgl@sss.pgh.pa.us 443 :CBC 8299 : get_sortgroupref_clause_noerr(Index sortref, List *clauses)
444 : : {
445 : : ListCell *l;
446 : :
447 [ + + + + : 13419 : foreach(l, clauses)
+ + ]
448 : : {
449 : 11650 : SortGroupClause *cl = (SortGroupClause *) lfirst(l);
450 : :
451 [ + + ]: 11650 : if (cl->tleSortGroupRef == sortref)
452 : 6530 : return cl;
453 : : }
454 : :
455 : 1769 : return NULL;
456 : : }
457 : :
458 : : /*
459 : : * extract_grouping_ops - make an array of the equality operator OIDs
460 : : * for a SortGroupClause list
461 : : */
462 : : Oid *
5729 463 : 19368 : extract_grouping_ops(List *groupClause)
464 : : {
465 : 19368 : int numCols = list_length(groupClause);
466 : 19368 : int colno = 0;
467 : : Oid *groupOperators;
468 : : ListCell *glitem;
469 : :
470 : 19368 : groupOperators = (Oid *) palloc(sizeof(Oid) * numCols);
471 : :
472 [ + + + + : 24439 : foreach(glitem, groupClause)
+ + ]
473 : : {
474 : 5071 : SortGroupClause *groupcl = (SortGroupClause *) lfirst(glitem);
475 : :
476 : 5071 : groupOperators[colno] = groupcl->eqop;
477 [ - + ]: 5071 : Assert(OidIsValid(groupOperators[colno]));
478 : 5071 : colno++;
479 : : }
480 : :
481 : 19368 : return groupOperators;
482 : : }
483 : :
484 : : /*
485 : : * extract_grouping_collations - make an array of the grouping column collations
486 : : * for a SortGroupClause list
487 : : */
488 : : Oid *
1850 peter@eisentraut.org 489 : 19368 : extract_grouping_collations(List *groupClause, List *tlist)
490 : : {
491 : 19368 : int numCols = list_length(groupClause);
492 : 19368 : int colno = 0;
493 : : Oid *grpCollations;
494 : : ListCell *glitem;
495 : :
496 : 19368 : grpCollations = (Oid *) palloc(sizeof(Oid) * numCols);
497 : :
498 [ + + + + : 24439 : foreach(glitem, groupClause)
+ + ]
499 : : {
500 : 5071 : SortGroupClause *groupcl = (SortGroupClause *) lfirst(glitem);
501 : 5071 : TargetEntry *tle = get_sortgroupclause_tle(groupcl, tlist);
502 : :
503 : 5071 : grpCollations[colno++] = exprCollation((Node *) tle->expr);
504 : : }
505 : :
506 : 19368 : return grpCollations;
507 : : }
508 : :
509 : : /*
510 : : * extract_grouping_cols - make an array of the grouping column resnos
511 : : * for a SortGroupClause list
512 : : */
513 : : AttrNumber *
5729 tgl@sss.pgh.pa.us 514 : 18601 : extract_grouping_cols(List *groupClause, List *tlist)
515 : : {
516 : : AttrNumber *grpColIdx;
517 : 18601 : int numCols = list_length(groupClause);
518 : 18601 : int colno = 0;
519 : : ListCell *glitem;
520 : :
521 : 18601 : grpColIdx = (AttrNumber *) palloc(sizeof(AttrNumber) * numCols);
522 : :
523 [ + + + + : 22611 : foreach(glitem, groupClause)
+ + ]
524 : : {
525 : 4010 : SortGroupClause *groupcl = (SortGroupClause *) lfirst(glitem);
526 : 4010 : TargetEntry *tle = get_sortgroupclause_tle(groupcl, tlist);
527 : :
528 : 4010 : grpColIdx[colno++] = tle->resno;
529 : : }
530 : :
531 : 18601 : return grpColIdx;
532 : : }
533 : :
534 : : /*
535 : : * grouping_is_sortable - is it possible to implement grouping list by sorting?
536 : : *
537 : : * This is easy since the parser will have included a sortop if one exists.
538 : : */
539 : : bool
540 : 27788 : grouping_is_sortable(List *groupClause)
541 : : {
542 : : ListCell *glitem;
543 : :
544 [ + + + + : 45287 : foreach(glitem, groupClause)
+ + ]
545 : : {
546 : 17542 : SortGroupClause *groupcl = (SortGroupClause *) lfirst(glitem);
547 : :
548 [ + + ]: 17542 : if (!OidIsValid(groupcl->sortop))
549 : 43 : return false;
550 : : }
551 : 27745 : return true;
552 : : }
553 : :
554 : : /*
555 : : * grouping_is_hashable - is it possible to implement grouping list by hashing?
556 : : *
557 : : * We rely on the parser to have set the hashable flag correctly.
558 : : */
559 : : bool
560 : 4779 : grouping_is_hashable(List *groupClause)
561 : : {
562 : : ListCell *glitem;
563 : :
564 [ + + + + : 14314 : foreach(glitem, groupClause)
+ + ]
565 : : {
566 : 9606 : SortGroupClause *groupcl = (SortGroupClause *) lfirst(glitem);
567 : :
4915 568 [ + + ]: 9606 : if (!groupcl->hashable)
5729 569 : 71 : return false;
570 : : }
571 : 4708 : return true;
572 : : }
573 : :
574 : :
575 : : /*****************************************************************************
576 : : * PathTarget manipulation functions
577 : : *
578 : : * PathTarget is a somewhat stripped-down version of a full targetlist; it
579 : : * omits all the TargetEntry decoration except (optionally) sortgroupref data,
580 : : * and it adds evaluation cost and output data width info.
581 : : *****************************************************************************/
582 : :
583 : : /*
584 : : * make_pathtarget_from_tlist
585 : : * Construct a PathTarget equivalent to the given targetlist.
586 : : *
587 : : * This leaves the cost and width fields as zeroes. Most callers will want
588 : : * to use create_pathtarget(), so as to get those set.
589 : : */
590 : : PathTarget *
2960 591 : 254876 : make_pathtarget_from_tlist(List *tlist)
592 : : {
2953 593 : 254876 : PathTarget *target = makeNode(PathTarget);
594 : : int i;
595 : : ListCell *lc;
596 : :
2960 597 : 254876 : target->sortgrouprefs = (Index *) palloc(list_length(tlist) * sizeof(Index));
598 : :
599 : 254876 : i = 0;
600 [ + + + + : 877614 : foreach(lc, tlist)
+ + ]
601 : : {
602 : 622738 : TargetEntry *tle = (TargetEntry *) lfirst(lc);
603 : :
604 : 622738 : target->exprs = lappend(target->exprs, tle->expr);
605 : 622738 : target->sortgrouprefs[i] = tle->ressortgroupref;
606 : 622738 : i++;
607 : : }
608 : :
609 : : /*
610 : : * Mark volatility as unknown. The contain_volatile_functions function
611 : : * will determine if there are any volatile functions when called for the
612 : : * first time with this PathTarget.
613 : : */
1112 drowley@postgresql.o 614 : 254876 : target->has_volatile_expr = VOLATILITY_UNKNOWN;
615 : :
2960 tgl@sss.pgh.pa.us 616 : 254876 : return target;
617 : : }
618 : :
619 : : /*
620 : : * make_tlist_from_pathtarget
621 : : * Construct a targetlist from a PathTarget.
622 : : */
623 : : List *
624 : 19381 : make_tlist_from_pathtarget(PathTarget *target)
625 : : {
626 : 19381 : List *tlist = NIL;
627 : : int i;
628 : : ListCell *lc;
629 : :
630 : 19381 : i = 0;
631 [ + + + + : 78559 : foreach(lc, target->exprs)
+ + ]
632 : : {
633 : 59178 : Expr *expr = (Expr *) lfirst(lc);
634 : : TargetEntry *tle;
635 : :
636 : 59178 : tle = makeTargetEntry(expr,
637 : 59178 : i + 1,
638 : : NULL,
639 : : false);
640 [ + - ]: 59178 : if (target->sortgrouprefs)
641 : 59178 : tle->ressortgroupref = target->sortgrouprefs[i];
642 : 59178 : tlist = lappend(tlist, tle);
643 : 59178 : i++;
644 : : }
645 : :
646 : 19381 : return tlist;
647 : : }
648 : :
649 : : /*
650 : : * copy_pathtarget
651 : : * Copy a PathTarget.
652 : : *
653 : : * The new PathTarget has its own exprs List, but shares the underlying
654 : : * target expression trees with the old one.
655 : : */
656 : : PathTarget *
2958 657 : 12315 : copy_pathtarget(PathTarget *src)
658 : : {
2953 659 : 12315 : PathTarget *dst = makeNode(PathTarget);
660 : :
661 : : /* Copy scalar fields */
2958 662 : 12315 : memcpy(dst, src, sizeof(PathTarget));
663 : : /* Shallow-copy the expression list */
664 : 12315 : dst->exprs = list_copy(src->exprs);
665 : : /* Duplicate sortgrouprefs if any (if not, the memcpy handled this) */
666 [ + + ]: 12315 : if (src->sortgrouprefs)
667 : : {
668 : 11721 : Size nbytes = list_length(src->exprs) * sizeof(Index);
669 : :
670 : 11721 : dst->sortgrouprefs = (Index *) palloc(nbytes);
671 : 11721 : memcpy(dst->sortgrouprefs, src->sortgrouprefs, nbytes);
672 : : }
673 : 12315 : return dst;
674 : : }
675 : :
676 : : /*
677 : : * create_empty_pathtarget
678 : : * Create an empty (zero columns, zero cost) PathTarget.
679 : : */
680 : : PathTarget *
2956 681 : 765101 : create_empty_pathtarget(void)
682 : : {
683 : : /* This is easy, but we don't want callers to hard-wire this ... */
2953 684 : 765101 : return makeNode(PathTarget);
685 : : }
686 : :
687 : : /*
688 : : * add_column_to_pathtarget
689 : : * Append a target column to the PathTarget.
690 : : *
691 : : * As with make_pathtarget_from_tlist, we leave it to the caller to update
692 : : * the cost and width fields.
693 : : */
694 : : void
2958 695 : 24870 : add_column_to_pathtarget(PathTarget *target, Expr *expr, Index sortgroupref)
696 : : {
697 : : /* Updating the exprs list is easy ... */
698 : 24870 : target->exprs = lappend(target->exprs, expr);
699 : : /* ... the sortgroupref data, a bit less so */
700 [ + + ]: 24870 : if (target->sortgrouprefs)
701 : : {
702 : 6155 : int nexprs = list_length(target->exprs);
703 : :
704 : : /* This might look inefficient, but actually it's usually cheap */
705 : 6155 : target->sortgrouprefs = (Index *)
706 : 6155 : repalloc(target->sortgrouprefs, nexprs * sizeof(Index));
707 : 6155 : target->sortgrouprefs[nexprs - 1] = sortgroupref;
708 : : }
709 [ + + ]: 18715 : else if (sortgroupref)
710 : : {
711 : : /* Adding sortgroupref labeling to a previously unlabeled target */
712 : 3916 : int nexprs = list_length(target->exprs);
713 : :
714 : 3916 : target->sortgrouprefs = (Index *) palloc0(nexprs * sizeof(Index));
715 : 3916 : target->sortgrouprefs[nexprs - 1] = sortgroupref;
716 : : }
717 : :
718 : : /*
719 : : * Reset has_volatile_expr to UNKNOWN. We just leave it up to
720 : : * contain_volatile_functions to set this properly again. Technically we
721 : : * could save some effort here and just check the new Expr, but it seems
722 : : * better to keep the logic for setting this flag in one location rather
723 : : * than duplicating the logic here.
724 : : */
1112 drowley@postgresql.o 725 [ - + ]: 24870 : if (target->has_volatile_expr == VOLATILITY_NOVOLATILE)
1112 drowley@postgresql.o 726 :UBC 0 : target->has_volatile_expr = VOLATILITY_UNKNOWN;
2958 tgl@sss.pgh.pa.us 727 :CBC 24870 : }
728 : :
729 : : /*
730 : : * add_new_column_to_pathtarget
731 : : * Append a target column to the PathTarget, but only if it's not
732 : : * equal() to any pre-existing target expression.
733 : : *
734 : : * The caller cannot specify a sortgroupref, since it would be unclear how
735 : : * to merge that with a pre-existing column.
736 : : *
737 : : * As with make_pathtarget_from_tlist, we leave it to the caller to update
738 : : * the cost and width fields.
739 : : */
740 : : void
2956 741 : 20955 : add_new_column_to_pathtarget(PathTarget *target, Expr *expr)
742 : : {
743 [ + + ]: 20955 : if (!list_member(target->exprs, expr))
744 : 16586 : add_column_to_pathtarget(target, expr, 0);
745 : 20955 : }
746 : :
747 : : /*
748 : : * add_new_columns_to_pathtarget
749 : : * Apply add_new_column_to_pathtarget() for each element of the list.
750 : : */
751 : : void
752 : 20371 : add_new_columns_to_pathtarget(PathTarget *target, List *exprs)
753 : : {
754 : : ListCell *lc;
755 : :
756 [ + + + + : 41326 : foreach(lc, exprs)
+ + ]
757 : : {
758 : 20955 : Expr *expr = (Expr *) lfirst(lc);
759 : :
760 : 20955 : add_new_column_to_pathtarget(target, expr);
761 : : }
762 : 20371 : }
763 : :
764 : : /*
765 : : * apply_pathtarget_labeling_to_tlist
766 : : * Apply any sortgrouprefs in the PathTarget to matching tlist entries
767 : : *
768 : : * Here, we do not assume that the tlist entries are one-for-one with the
769 : : * PathTarget. The intended use of this function is to deal with cases
770 : : * where createplan.c has decided to use some other tlist and we have
771 : : * to identify what matches exist.
772 : : */
773 : : void
2960 774 : 7150 : apply_pathtarget_labeling_to_tlist(List *tlist, PathTarget *target)
775 : : {
776 : : int i;
777 : : ListCell *lc;
778 : :
779 : : /* Nothing to do if PathTarget has no sortgrouprefs data */
780 [ + + ]: 7150 : if (target->sortgrouprefs == NULL)
781 : 6444 : return;
782 : :
783 : 706 : i = 0;
784 [ + - + + : 2083 : foreach(lc, target->exprs)
+ + ]
785 : : {
786 : 1377 : Expr *expr = (Expr *) lfirst(lc);
787 : : TargetEntry *tle;
788 : :
789 [ + + ]: 1377 : if (target->sortgrouprefs[i])
790 : : {
791 : : /*
792 : : * For Vars, use tlist_member_match_var's weakened matching rule;
793 : : * this allows us to deal with some cases where a set-returning
794 : : * function has been inlined, so that we now have more knowledge
795 : : * about what it returns than we did when the original Var was
796 : : * created. Otherwise, use regular equal() to find the matching
797 : : * TLE. (In current usage, only the Var case is actually needed;
798 : : * but it seems best to have sane behavior here for non-Vars too.)
799 : : */
800 [ + - + - ]: 1060 : if (expr && IsA(expr, Var))
801 : 1060 : tle = tlist_member_match_var((Var *) expr, tlist);
802 : : else
2593 peter_e@gmx.net 803 :UBC 0 : tle = tlist_member(expr, tlist);
804 : :
805 : : /*
806 : : * Complain if noplace for the sortgrouprefs label, or if we'd
807 : : * have to label a column twice. (The case where it already has
808 : : * the desired label probably can't happen, but we may as well
809 : : * allow for it.)
810 : : */
2880 tgl@sss.pgh.pa.us 811 [ - + ]:CBC 1060 : if (!tle)
2880 tgl@sss.pgh.pa.us 812 [ # # ]:UBC 0 : elog(ERROR, "ORDER/GROUP BY expression not found in targetlist");
2880 tgl@sss.pgh.pa.us 813 [ - + ]:CBC 1060 : if (tle->ressortgroupref != 0 &&
2880 tgl@sss.pgh.pa.us 814 [ # # ]:UBC 0 : tle->ressortgroupref != target->sortgrouprefs[i])
815 [ # # ]: 0 : elog(ERROR, "targetlist item has multiple sortgroupref labels");
816 : :
2880 tgl@sss.pgh.pa.us 817 :CBC 1060 : tle->ressortgroupref = target->sortgrouprefs[i];
818 : : }
2960 819 : 1377 : i++;
820 : : }
821 : : }
822 : :
823 : : /*
824 : : * split_pathtarget_at_srfs
825 : : * Split given PathTarget into multiple levels to position SRFs safely
826 : : *
827 : : * The executor can only handle set-returning functions that appear at the
828 : : * top level of the targetlist of a ProjectSet plan node. If we have any SRFs
829 : : * that are not at top level, we need to split up the evaluation into multiple
830 : : * plan levels in which each level satisfies this constraint. This function
831 : : * creates appropriate PathTarget(s) for each level.
832 : : *
833 : : * As an example, consider the tlist expression
834 : : * x + srf1(srf2(y + z))
835 : : * This expression should appear as-is in the top PathTarget, but below that
836 : : * we must have a PathTarget containing
837 : : * x, srf1(srf2(y + z))
838 : : * and below that, another PathTarget containing
839 : : * x, srf2(y + z)
840 : : * and below that, another PathTarget containing
841 : : * x, y, z
842 : : * When these tlists are processed by setrefs.c, subexpressions that match
843 : : * output expressions of the next lower tlist will be replaced by Vars,
844 : : * so that what the executor gets are tlists looking like
845 : : * Var1 + Var2
846 : : * Var1, srf1(Var2)
847 : : * Var1, srf2(Var2 + Var3)
848 : : * x, y, z
849 : : * which satisfy the desired property.
850 : : *
851 : : * Another example is
852 : : * srf1(x), srf2(srf3(y))
853 : : * That must appear as-is in the top PathTarget, but below that we need
854 : : * srf1(x), srf3(y)
855 : : * That is, each SRF must be computed at a level corresponding to the nesting
856 : : * depth of SRFs within its arguments.
857 : : *
858 : : * In some cases, a SRF has already been evaluated in some previous plan level
859 : : * and we shouldn't expand it again (that is, what we see in the target is
860 : : * already meant as a reference to a lower subexpression). So, don't expand
861 : : * any tlist expressions that appear in input_target, if that's not NULL.
862 : : *
863 : : * It's also important that we preserve any sortgroupref annotation appearing
864 : : * in the given target, especially on expressions matching input_target items.
865 : : *
866 : : * The outputs of this function are two parallel lists, one a list of
867 : : * PathTargets and the other an integer list of bool flags indicating
868 : : * whether the corresponding PathTarget contains any evaluable SRFs.
869 : : * The lists are given in the order they'd need to be evaluated in, with
870 : : * the "lowest" PathTarget first. So the last list entry is always the
871 : : * originally given PathTarget, and any entries before it indicate evaluation
872 : : * levels that must be inserted below it. The first list entry must not
873 : : * contain any SRFs (other than ones duplicating input_target entries), since
874 : : * it will typically be attached to a plan node that cannot evaluate SRFs.
875 : : *
876 : : * Note: using a list for the flags may seem like overkill, since there
877 : : * are only a few possible patterns for which levels contain SRFs.
878 : : * But this representation decouples callers from that knowledge.
879 : : */
880 : : void
2643 andres@anarazel.de 881 : 16808 : split_pathtarget_at_srfs(PlannerInfo *root,
882 : : PathTarget *target, PathTarget *input_target,
883 : : List **targets, List **targets_contain_srfs)
884 : : {
885 : : split_pathtarget_context context;
886 : : int max_depth;
887 : : bool need_extra_projection;
888 : : List *prev_level_tlist;
889 : : int lci;
890 : : ListCell *lc,
891 : : *lc1,
892 : : *lc2,
893 : : *lc3;
894 : :
895 : : /*
896 : : * It's not unusual for planner.c to pass us two physically identical
897 : : * targets, in which case we can conclude without further ado that all
898 : : * expressions are available from the input. (The logic below would
899 : : * arrive at the same conclusion, but much more tediously.)
900 : : */
2628 tgl@sss.pgh.pa.us 901 [ + + ]: 16808 : if (target == input_target)
902 : : {
903 : 12373 : *targets = list_make1(target);
904 : 12373 : *targets_contain_srfs = list_make1_int(false);
905 : 12373 : return;
906 : : }
907 : :
908 : : /* Pass any input_target exprs down to split_pathtarget_walker() */
909 [ + + ]: 4435 : context.input_target_exprs = input_target ? input_target->exprs : NIL;
910 : :
911 : : /*
912 : : * Initialize with empty level-zero lists, and no levels after that.
913 : : * (Note: we could dispense with representing level zero explicitly, since
914 : : * it will never receive any SRFs, but then we'd have to special-case that
915 : : * level when we get to building result PathTargets. Level zero describes
916 : : * the SRF-free PathTarget that will be given to the input plan node.)
917 : : */
918 : 4435 : context.level_srfs = list_make1(NIL);
919 : 4435 : context.level_input_vars = list_make1(NIL);
920 : 4435 : context.level_input_srfs = list_make1(NIL);
921 : :
922 : : /* Initialize data we'll accumulate across all the target expressions */
923 : 4435 : context.current_input_vars = NIL;
924 : 4435 : context.current_input_srfs = NIL;
925 : 4435 : max_depth = 0;
926 : 4435 : need_extra_projection = false;
927 : :
928 : : /* Scan each expression in the PathTarget looking for SRFs */
2124 929 : 4435 : lci = 0;
2628 930 [ + - + + : 10547 : foreach(lc, target->exprs)
+ + ]
931 : : {
932 : 6112 : Node *node = (Node *) lfirst(lc);
933 : :
934 : : /* Tell split_pathtarget_walker about this expr's sortgroupref */
2124 935 [ + + ]: 6112 : context.current_sgref = get_pathtarget_sortgroupref(target, lci);
936 : 6112 : lci++;
937 : :
938 : : /*
939 : : * Find all SRFs and Vars (and Var-like nodes) in this expression, and
940 : : * enter them into appropriate lists within the context struct.
941 : : */
2628 942 : 6112 : context.current_depth = 0;
943 : 6112 : split_pathtarget_walker(node, &context);
944 : :
945 : : /* An expression containing no SRFs is of no further interest */
946 [ + + ]: 6112 : if (context.current_depth == 0)
947 : 1240 : continue;
948 : :
949 : : /*
950 : : * Track max SRF nesting depth over the whole PathTarget. Also, if
951 : : * this expression establishes a new max depth, we no longer care
952 : : * whether previous expressions contained nested SRFs; we can handle
953 : : * any required projection for them in the final ProjectSet node.
954 : : */
955 [ + + ]: 4872 : if (max_depth < context.current_depth)
956 : : {
957 : 4208 : max_depth = context.current_depth;
958 : 4208 : need_extra_projection = false;
959 : : }
960 : :
961 : : /*
962 : : * If any maximum-depth SRF is not at the top level of its expression,
963 : : * we'll need an extra Result node to compute the top-level scalar
964 : : * expression.
965 : : */
966 [ + + + + : 4872 : if (max_depth == context.current_depth && !IS_SRF_CALL(node))
+ + + + +
+ ]
967 : 1087 : need_extra_projection = true;
968 : : }
969 : :
970 : : /*
971 : : * If we found no SRFs needing evaluation (maybe they were all present in
972 : : * input_target, or maybe they were all removed by const-simplification),
973 : : * then no ProjectSet is needed; fall out.
974 : : */
975 [ + + ]: 4435 : if (max_depth == 0)
976 : : {
977 : 233 : *targets = list_make1(target);
978 : 233 : *targets_contain_srfs = list_make1_int(false);
979 : 233 : return;
980 : : }
981 : :
982 : : /*
983 : : * The Vars and SRF outputs needed at top level can be added to the last
984 : : * level_input lists if we don't need an extra projection step. If we do
985 : : * need one, add a SRF-free level to the lists.
986 : : */
987 [ + + ]: 4202 : if (need_extra_projection)
988 : : {
989 : 517 : context.level_srfs = lappend(context.level_srfs, NIL);
990 : 1034 : context.level_input_vars = lappend(context.level_input_vars,
991 : 517 : context.current_input_vars);
992 : 517 : context.level_input_srfs = lappend(context.level_input_srfs,
993 : 517 : context.current_input_srfs);
994 : : }
995 : : else
996 : : {
997 : 3685 : lc = list_nth_cell(context.level_input_vars, max_depth);
998 : 3685 : lfirst(lc) = list_concat(lfirst(lc), context.current_input_vars);
999 : 3685 : lc = list_nth_cell(context.level_input_srfs, max_depth);
1000 : 3685 : lfirst(lc) = list_concat(lfirst(lc), context.current_input_srfs);
1001 : : }
1002 : :
1003 : : /*
1004 : : * Now construct the output PathTargets. The original target can be used
1005 : : * as-is for the last one, but we need to construct a new SRF-free target
1006 : : * representing what the preceding plan node has to emit, as well as a
1007 : : * target for each intermediate ProjectSet node.
1008 : : */
1009 : 4202 : *targets = *targets_contain_srfs = NIL;
1010 : 4202 : prev_level_tlist = NIL;
1011 : :
1012 [ + - + + : 13153 : forthree(lc1, context.level_srfs,
+ - + + +
- + + + +
+ - + - +
+ ]
1013 : : lc2, context.level_input_vars,
1014 : : lc3, context.level_input_srfs)
1015 : : {
1016 : 8951 : List *level_srfs = (List *) lfirst(lc1);
1017 : : PathTarget *ntarget;
1018 : :
1735 1019 [ + + ]: 8951 : if (lnext(context.level_srfs, lc1) == NULL)
1020 : : {
2628 1021 : 4202 : ntarget = target;
1022 : : }
1023 : : else
1024 : : {
1025 : 4749 : ntarget = create_empty_pathtarget();
1026 : :
1027 : : /*
1028 : : * This target should actually evaluate any SRFs of the current
1029 : : * level, and it needs to propagate forward any Vars needed by
1030 : : * later levels, as well as SRFs computed earlier and needed by
1031 : : * later levels.
1032 : : */
2124 1033 : 4749 : add_sp_items_to_pathtarget(ntarget, level_srfs);
1735 1034 [ + - + + : 10063 : for_each_cell(lc, context.level_input_vars,
+ + ]
1035 : : lnext(context.level_input_vars, lc2))
1036 : : {
2628 1037 : 5314 : List *input_vars = (List *) lfirst(lc);
1038 : :
2124 1039 : 5314 : add_sp_items_to_pathtarget(ntarget, input_vars);
1040 : : }
1735 1041 [ + - + + : 10063 : for_each_cell(lc, context.level_input_srfs,
+ + ]
1042 : : lnext(context.level_input_srfs, lc3))
1043 : : {
2628 1044 : 5314 : List *input_srfs = (List *) lfirst(lc);
1045 : : ListCell *lcx;
1046 : :
1047 [ + + + + : 11450 : foreach(lcx, input_srfs)
+ + ]
1048 : : {
2124 1049 : 6136 : split_pathtarget_item *item = lfirst(lcx);
1050 : :
1051 [ + + ]: 6136 : if (list_member(prev_level_tlist, item->expr))
1052 : 18 : add_sp_item_to_pathtarget(ntarget, item);
1053 : : }
1054 : : }
2628 1055 : 4749 : set_pathtarget_cost_width(root, ntarget);
1056 : : }
1057 : :
1058 : : /*
1059 : : * Add current target and does-it-compute-SRFs flag to output lists.
1060 : : */
1061 : 8951 : *targets = lappend(*targets, ntarget);
1062 : 8951 : *targets_contain_srfs = lappend_int(*targets_contain_srfs,
1063 : : (level_srfs != NIL));
1064 : :
1065 : : /* Remember this level's output for next pass */
1066 : 8951 : prev_level_tlist = ntarget->exprs;
1067 : : }
1068 : : }
1069 : :
1070 : : /*
1071 : : * Recursively examine expressions for split_pathtarget_at_srfs.
1072 : : *
1073 : : * Note we make no effort here to prevent duplicate entries in the output
1074 : : * lists. Duplicates will be gotten rid of later.
1075 : : */
1076 : : static bool
2643 andres@anarazel.de 1077 : 20956 : split_pathtarget_walker(Node *node, split_pathtarget_context *context)
1078 : : {
1079 [ + + ]: 20956 : if (node == NULL)
1080 : 47 : return false;
1081 : :
1082 : : /*
1083 : : * A subexpression that matches an expression already computed in
1084 : : * input_target can be treated like a Var (which indeed it will be after
1085 : : * setrefs.c gets done with it), even if it's actually a SRF. Record it
1086 : : * as being needed for the current expression, and ignore any
1087 : : * substructure. (Note in particular that this preserves the identity of
1088 : : * any expressions that appear as sortgrouprefs in input_target.)
1089 : : */
2628 tgl@sss.pgh.pa.us 1090 [ + + ]: 20909 : if (list_member(context->input_target_exprs, node))
1091 : : {
2124 1092 : 186 : split_pathtarget_item *item = palloc(sizeof(split_pathtarget_item));
1093 : :
1094 : 186 : item->expr = node;
1095 : 186 : item->sortgroupref = context->current_sgref;
2628 1096 : 186 : context->current_input_vars = lappend(context->current_input_vars,
1097 : : item);
1098 : 186 : return false;
1099 : : }
1100 : :
1101 : : /*
1102 : : * Vars and Var-like constructs are expected to be gotten from the input,
1103 : : * too. We assume that these constructs cannot contain any SRFs (if one
1104 : : * does, there will be an executor failure from a misplaced SRF).
1105 : : */
2643 andres@anarazel.de 1106 [ + + ]: 20723 : if (IsA(node, Var) ||
1107 [ + - ]: 19192 : IsA(node, PlaceHolderVar) ||
1108 [ + + ]: 19192 : IsA(node, Aggref) ||
1109 [ + - ]: 18599 : IsA(node, GroupingFunc) ||
1110 [ + + ]: 18599 : IsA(node, WindowFunc))
1111 : : {
2124 tgl@sss.pgh.pa.us 1112 : 2133 : split_pathtarget_item *item = palloc(sizeof(split_pathtarget_item));
1113 : :
1114 : 2133 : item->expr = node;
1115 : 2133 : item->sortgroupref = context->current_sgref;
2628 1116 : 2133 : context->current_input_vars = lappend(context->current_input_vars,
1117 : : item);
2643 andres@anarazel.de 1118 : 2133 : return false;
1119 : : }
1120 : :
1121 : : /*
1122 : : * If it's a SRF, recursively examine its inputs, determine its level, and
1123 : : * make appropriate entries in the output lists.
1124 : : */
2628 tgl@sss.pgh.pa.us 1125 [ + + + + : 18590 : if (IS_SRF_CALL(node))
+ + + + ]
1126 : : {
2124 1127 : 4905 : split_pathtarget_item *item = palloc(sizeof(split_pathtarget_item));
2628 1128 : 4905 : List *save_input_vars = context->current_input_vars;
1129 : 4905 : List *save_input_srfs = context->current_input_srfs;
1130 : 4905 : int save_current_depth = context->current_depth;
1131 : : int srf_depth;
1132 : : ListCell *lc;
1133 : :
2124 1134 : 4905 : item->expr = node;
1135 : 4905 : item->sortgroupref = context->current_sgref;
1136 : :
2628 1137 : 4905 : context->current_input_vars = NIL;
1138 : 4905 : context->current_input_srfs = NIL;
1139 : 4905 : context->current_depth = 0;
2124 1140 : 4905 : context->current_sgref = 0; /* subexpressions are not sortgroup items */
1141 : :
2628 1142 : 4905 : (void) expression_tree_walker(node, split_pathtarget_walker,
1143 : : (void *) context);
1144 : :
1145 : : /* Depth is one more than any SRF below it */
1146 : 4905 : srf_depth = context->current_depth + 1;
1147 : :
1148 : : /* If new record depth, initialize another level of output lists */
1149 [ + + ]: 4905 : if (srf_depth >= list_length(context->level_srfs))
1150 : : {
1151 : 4232 : context->level_srfs = lappend(context->level_srfs, NIL);
1152 : 4232 : context->level_input_vars = lappend(context->level_input_vars, NIL);
1153 : 4232 : context->level_input_srfs = lappend(context->level_input_srfs, NIL);
1154 : : }
1155 : :
1156 : : /* Record this SRF as needing to be evaluated at appropriate level */
1157 : 4905 : lc = list_nth_cell(context->level_srfs, srf_depth);
2124 1158 : 4905 : lfirst(lc) = lappend(lfirst(lc), item);
1159 : :
1160 : : /* Record its inputs as being needed at the same level */
2628 1161 : 4905 : lc = list_nth_cell(context->level_input_vars, srf_depth);
1162 : 4905 : lfirst(lc) = list_concat(lfirst(lc), context->current_input_vars);
1163 : 4905 : lc = list_nth_cell(context->level_input_srfs, srf_depth);
1164 : 4905 : lfirst(lc) = list_concat(lfirst(lc), context->current_input_srfs);
1165 : :
1166 : : /*
1167 : : * Restore caller-level state and update it for presence of this SRF.
1168 : : * Notice we report the SRF itself as being needed for evaluation of
1169 : : * surrounding expression.
1170 : : */
1171 : 4905 : context->current_input_vars = save_input_vars;
2124 1172 : 4905 : context->current_input_srfs = lappend(save_input_srfs, item);
2628 1173 : 4905 : context->current_depth = Max(save_current_depth, srf_depth);
1174 : :
1175 : : /* We're done here */
2643 andres@anarazel.de 1176 : 4905 : return false;
1177 : : }
1178 : :
1179 : : /*
1180 : : * Otherwise, the node is a scalar (non-set) expression, so recurse to
1181 : : * examine its inputs.
1182 : : */
2124 tgl@sss.pgh.pa.us 1183 : 13685 : context->current_sgref = 0; /* subexpressions are not sortgroup items */
2643 andres@anarazel.de 1184 : 13685 : return expression_tree_walker(node, split_pathtarget_walker,
1185 : : (void *) context);
1186 : : }
1187 : :
1188 : : /*
1189 : : * Add a split_pathtarget_item to the PathTarget, unless a matching item is
1190 : : * already present. This is like add_new_column_to_pathtarget, but allows
1191 : : * for sortgrouprefs to be handled. An item having zero sortgroupref can
1192 : : * be merged with one that has a sortgroupref, acquiring the latter's
1193 : : * sortgroupref.
1194 : : *
1195 : : * Note that we don't worry about possibly adding duplicate sortgrouprefs
1196 : : * to the PathTarget. That would be bad, but it should be impossible unless
1197 : : * the target passed to split_pathtarget_at_srfs already had duplicates.
1198 : : * As long as it didn't, we can have at most one split_pathtarget_item with
1199 : : * any particular nonzero sortgroupref.
1200 : : */
1201 : : static void
2124 tgl@sss.pgh.pa.us 1202 : 3350 : add_sp_item_to_pathtarget(PathTarget *target, split_pathtarget_item *item)
1203 : : {
1204 : : int lci;
1205 : : ListCell *lc;
1206 : :
1207 : : /*
1208 : : * Look for a pre-existing entry that is equal() and does not have a
1209 : : * conflicting sortgroupref already.
1210 : : */
1211 : 3350 : lci = 0;
1212 [ + + + + : 4714 : foreach(lc, target->exprs)
+ + ]
1213 : : {
1214 : 2732 : Node *node = (Node *) lfirst(lc);
1215 [ + + ]: 2732 : Index sgref = get_pathtarget_sortgroupref(target, lci);
1216 : :
1217 [ + + ]: 2732 : if ((item->sortgroupref == sgref ||
1218 [ + + + + ]: 138 : item->sortgroupref == 0 ||
1219 [ + + ]: 2705 : sgref == 0) &&
1220 : 2705 : equal(item->expr, node))
1221 : : {
1222 : : /* Found a match. Assign item's sortgroupref if it has one. */
1223 [ + + ]: 1368 : if (item->sortgroupref)
1224 : : {
1225 [ + - ]: 6 : if (target->sortgrouprefs == NULL)
1226 : : {
1227 : 6 : target->sortgrouprefs = (Index *)
1228 : 6 : palloc0(list_length(target->exprs) * sizeof(Index));
1229 : : }
1230 : 6 : target->sortgrouprefs[lci] = item->sortgroupref;
1231 : : }
1232 : 1368 : return;
1233 : : }
1234 : 1364 : lci++;
1235 : : }
1236 : :
1237 : : /*
1238 : : * No match, so add item to PathTarget. Copy the expr for safety.
1239 : : */
1240 : 1982 : add_column_to_pathtarget(target, (Expr *) copyObject(item->expr),
1241 : : item->sortgroupref);
1242 : : }
1243 : :
1244 : : /*
1245 : : * Apply add_sp_item_to_pathtarget to each element of list.
1246 : : */
1247 : : static void
1248 : 10063 : add_sp_items_to_pathtarget(PathTarget *target, List *items)
1249 : : {
1250 : : ListCell *lc;
1251 : :
1252 [ + + + + : 13395 : foreach(lc, items)
+ + ]
1253 : : {
1254 : 3332 : split_pathtarget_item *item = lfirst(lc);
1255 : :
1256 : 3332 : add_sp_item_to_pathtarget(target, item);
1257 : : }
1258 : 10063 : }
|