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