1 /*-------------------------------------------------------------------------
2  *
3  * tlist.c
4  *	  Target list manipulation routines
5  *
6  * Portions Copyright (c) 1996-2016, 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/tlist.h"
20 
21 
22 /*****************************************************************************
23  *		Target list creation and searching utilities
24  *****************************************************************************/
25 
26 /*
27  * tlist_member
28  *	  Finds the (first) member of the given tlist whose expression is
29  *	  equal() to the given expression.  Result is NULL if no such member.
30  */
31 TargetEntry *
tlist_member(Node * node,List * targetlist)32 tlist_member(Node *node, List *targetlist)
33 {
34 	ListCell   *temp;
35 
36 	foreach(temp, targetlist)
37 	{
38 		TargetEntry *tlentry = (TargetEntry *) lfirst(temp);
39 
40 		if (equal(node, tlentry->expr))
41 			return tlentry;
42 	}
43 	return NULL;
44 }
45 
46 /*
47  * tlist_member_ignore_relabel
48  *	  Same as above, except that we ignore top-level RelabelType nodes
49  *	  while checking for a match.  This is needed for some scenarios
50  *	  involving binary-compatible sort operations.
51  */
52 TargetEntry *
tlist_member_ignore_relabel(Node * node,List * targetlist)53 tlist_member_ignore_relabel(Node *node, List *targetlist)
54 {
55 	ListCell   *temp;
56 
57 	while (node && IsA(node, RelabelType))
58 		node = (Node *) ((RelabelType *) node)->arg;
59 
60 	foreach(temp, targetlist)
61 	{
62 		TargetEntry *tlentry = (TargetEntry *) lfirst(temp);
63 		Expr	   *tlexpr = tlentry->expr;
64 
65 		while (tlexpr && IsA(tlexpr, RelabelType))
66 			tlexpr = ((RelabelType *) tlexpr)->arg;
67 
68 		if (equal(node, tlexpr))
69 			return tlentry;
70 	}
71 	return NULL;
72 }
73 
74 /*
75  * tlist_member_match_var
76  *	  Same as above, except that we match the provided Var on the basis
77  *	  of varno/varattno/varlevelsup/vartype only, rather than full equal().
78  *
79  * This is needed in some cases where we can't be sure of an exact typmod
80  * match.  For safety, though, we insist on vartype match.
81  */
82 static TargetEntry *
tlist_member_match_var(Var * var,List * targetlist)83 tlist_member_match_var(Var *var, List *targetlist)
84 {
85 	ListCell   *temp;
86 
87 	foreach(temp, targetlist)
88 	{
89 		TargetEntry *tlentry = (TargetEntry *) lfirst(temp);
90 		Var		   *tlvar = (Var *) tlentry->expr;
91 
92 		if (!tlvar || !IsA(tlvar, Var))
93 			continue;
94 		if (var->varno == tlvar->varno &&
95 			var->varattno == tlvar->varattno &&
96 			var->varlevelsup == tlvar->varlevelsup &&
97 			var->vartype == tlvar->vartype)
98 			return tlentry;
99 	}
100 	return NULL;
101 }
102 
103 /*
104  * add_to_flat_tlist
105  *		Add more items to a flattened tlist (if they're not already in it)
106  *
107  * 'tlist' is the flattened tlist
108  * 'exprs' is a list of expressions (usually, but not necessarily, Vars)
109  *
110  * Returns the extended tlist.
111  */
112 List *
add_to_flat_tlist(List * tlist,List * exprs)113 add_to_flat_tlist(List *tlist, List *exprs)
114 {
115 	int			next_resno = list_length(tlist) + 1;
116 	ListCell   *lc;
117 
118 	foreach(lc, exprs)
119 	{
120 		Node	   *expr = (Node *) lfirst(lc);
121 
122 		if (!tlist_member(expr, tlist))
123 		{
124 			TargetEntry *tle;
125 
126 			tle = makeTargetEntry(copyObject(expr),		/* copy needed?? */
127 								  next_resno++,
128 								  NULL,
129 								  false);
130 			tlist = lappend(tlist, tle);
131 		}
132 	}
133 	return tlist;
134 }
135 
136 
137 /*
138  * get_tlist_exprs
139  *		Get just the expression subtrees of a tlist
140  *
141  * Resjunk columns are ignored unless includeJunk is true
142  */
143 List *
get_tlist_exprs(List * tlist,bool includeJunk)144 get_tlist_exprs(List *tlist, bool includeJunk)
145 {
146 	List	   *result = NIL;
147 	ListCell   *l;
148 
149 	foreach(l, tlist)
150 	{
151 		TargetEntry *tle = (TargetEntry *) lfirst(l);
152 
153 		if (tle->resjunk && !includeJunk)
154 			continue;
155 
156 		result = lappend(result, tle->expr);
157 	}
158 	return result;
159 }
160 
161 
162 /*
163  * count_nonjunk_tlist_entries
164  *		What it says ...
165  */
166 int
count_nonjunk_tlist_entries(List * tlist)167 count_nonjunk_tlist_entries(List *tlist)
168 {
169 	int			len = 0;
170 	ListCell   *l;
171 
172 	foreach(l, tlist)
173 	{
174 		TargetEntry *tle = (TargetEntry *) lfirst(l);
175 
176 		if (!tle->resjunk)
177 			len++;
178 	}
179 	return len;
180 }
181 
182 
183 /*
184  * tlist_same_exprs
185  *		Check whether two target lists contain the same expressions
186  *
187  * Note: this function is used to decide whether it's safe to jam a new tlist
188  * into a non-projection-capable plan node.  Obviously we can't do that unless
189  * the node's tlist shows it already returns the column values we want.
190  * However, we can ignore the TargetEntry attributes resname, ressortgroupref,
191  * resorigtbl, resorigcol, and resjunk, because those are only labelings that
192  * don't affect the row values computed by the node.  (Moreover, if we didn't
193  * ignore them, we'd frequently fail to make the desired optimization, since
194  * the planner tends to not bother to make resname etc. valid in intermediate
195  * plan nodes.)  Note that on success, the caller must still jam the desired
196  * tlist into the plan node, else it won't have the desired labeling fields.
197  */
198 bool
tlist_same_exprs(List * tlist1,List * tlist2)199 tlist_same_exprs(List *tlist1, List *tlist2)
200 {
201 	ListCell   *lc1,
202 			   *lc2;
203 
204 	if (list_length(tlist1) != list_length(tlist2))
205 		return false;			/* not same length, so can't match */
206 
207 	forboth(lc1, tlist1, lc2, tlist2)
208 	{
209 		TargetEntry *tle1 = (TargetEntry *) lfirst(lc1);
210 		TargetEntry *tle2 = (TargetEntry *) lfirst(lc2);
211 
212 		if (!equal(tle1->expr, tle2->expr))
213 			return false;
214 	}
215 
216 	return true;
217 }
218 
219 
220 /*
221  * Does tlist have same output datatypes as listed in colTypes?
222  *
223  * Resjunk columns are ignored if junkOK is true; otherwise presence of
224  * a resjunk column will always cause a 'false' result.
225  *
226  * Note: currently no callers care about comparing typmods.
227  */
228 bool
tlist_same_datatypes(List * tlist,List * colTypes,bool junkOK)229 tlist_same_datatypes(List *tlist, List *colTypes, bool junkOK)
230 {
231 	ListCell   *l;
232 	ListCell   *curColType = list_head(colTypes);
233 
234 	foreach(l, tlist)
235 	{
236 		TargetEntry *tle = (TargetEntry *) lfirst(l);
237 
238 		if (tle->resjunk)
239 		{
240 			if (!junkOK)
241 				return false;
242 		}
243 		else
244 		{
245 			if (curColType == NULL)
246 				return false;	/* tlist longer than colTypes */
247 			if (exprType((Node *) tle->expr) != lfirst_oid(curColType))
248 				return false;
249 			curColType = lnext(curColType);
250 		}
251 	}
252 	if (curColType != NULL)
253 		return false;			/* tlist shorter than colTypes */
254 	return true;
255 }
256 
257 /*
258  * Does tlist have same exposed collations as listed in colCollations?
259  *
260  * Identical logic to the above, but for collations.
261  */
262 bool
tlist_same_collations(List * tlist,List * colCollations,bool junkOK)263 tlist_same_collations(List *tlist, List *colCollations, bool junkOK)
264 {
265 	ListCell   *l;
266 	ListCell   *curColColl = list_head(colCollations);
267 
268 	foreach(l, tlist)
269 	{
270 		TargetEntry *tle = (TargetEntry *) lfirst(l);
271 
272 		if (tle->resjunk)
273 		{
274 			if (!junkOK)
275 				return false;
276 		}
277 		else
278 		{
279 			if (curColColl == NULL)
280 				return false;	/* tlist longer than colCollations */
281 			if (exprCollation((Node *) tle->expr) != lfirst_oid(curColColl))
282 				return false;
283 			curColColl = lnext(curColColl);
284 		}
285 	}
286 	if (curColColl != NULL)
287 		return false;			/* tlist shorter than colCollations */
288 	return true;
289 }
290 
291 /*
292  * apply_tlist_labeling
293  *		Apply the TargetEntry labeling attributes of src_tlist to dest_tlist
294  *
295  * This is useful for reattaching column names etc to a plan's final output
296  * targetlist.
297  */
298 void
apply_tlist_labeling(List * dest_tlist,List * src_tlist)299 apply_tlist_labeling(List *dest_tlist, List *src_tlist)
300 {
301 	ListCell   *ld,
302 			   *ls;
303 
304 	Assert(list_length(dest_tlist) == list_length(src_tlist));
305 	forboth(ld, dest_tlist, ls, src_tlist)
306 	{
307 		TargetEntry *dest_tle = (TargetEntry *) lfirst(ld);
308 		TargetEntry *src_tle = (TargetEntry *) lfirst(ls);
309 
310 		Assert(dest_tle->resno == src_tle->resno);
311 		dest_tle->resname = src_tle->resname;
312 		dest_tle->ressortgroupref = src_tle->ressortgroupref;
313 		dest_tle->resorigtbl = src_tle->resorigtbl;
314 		dest_tle->resorigcol = src_tle->resorigcol;
315 		dest_tle->resjunk = src_tle->resjunk;
316 	}
317 }
318 
319 
320 /*
321  * get_sortgroupref_tle
322  *		Find the targetlist entry matching the given SortGroupRef index,
323  *		and return it.
324  */
325 TargetEntry *
get_sortgroupref_tle(Index sortref,List * targetList)326 get_sortgroupref_tle(Index sortref, List *targetList)
327 {
328 	ListCell   *l;
329 
330 	foreach(l, targetList)
331 	{
332 		TargetEntry *tle = (TargetEntry *) lfirst(l);
333 
334 		if (tle->ressortgroupref == sortref)
335 			return tle;
336 	}
337 
338 	elog(ERROR, "ORDER/GROUP BY expression not found in targetlist");
339 	return NULL;				/* keep compiler quiet */
340 }
341 
342 /*
343  * get_sortgroupclause_tle
344  *		Find the targetlist entry matching the given SortGroupClause
345  *		by ressortgroupref, and return it.
346  */
347 TargetEntry *
get_sortgroupclause_tle(SortGroupClause * sgClause,List * targetList)348 get_sortgroupclause_tle(SortGroupClause *sgClause,
349 						List *targetList)
350 {
351 	return get_sortgroupref_tle(sgClause->tleSortGroupRef, targetList);
352 }
353 
354 /*
355  * get_sortgroupclause_expr
356  *		Find the targetlist entry matching the given SortGroupClause
357  *		by ressortgroupref, and return its expression.
358  */
359 Node *
get_sortgroupclause_expr(SortGroupClause * sgClause,List * targetList)360 get_sortgroupclause_expr(SortGroupClause *sgClause, List *targetList)
361 {
362 	TargetEntry *tle = get_sortgroupclause_tle(sgClause, targetList);
363 
364 	return (Node *) tle->expr;
365 }
366 
367 /*
368  * get_sortgrouplist_exprs
369  *		Given a list of SortGroupClauses, build a list
370  *		of the referenced targetlist expressions.
371  */
372 List *
get_sortgrouplist_exprs(List * sgClauses,List * targetList)373 get_sortgrouplist_exprs(List *sgClauses, List *targetList)
374 {
375 	List	   *result = NIL;
376 	ListCell   *l;
377 
378 	foreach(l, sgClauses)
379 	{
380 		SortGroupClause *sortcl = (SortGroupClause *) lfirst(l);
381 		Node	   *sortexpr;
382 
383 		sortexpr = get_sortgroupclause_expr(sortcl, targetList);
384 		result = lappend(result, sortexpr);
385 	}
386 	return result;
387 }
388 
389 
390 /*****************************************************************************
391  *		Functions to extract data from a list of SortGroupClauses
392  *
393  * These don't really belong in tlist.c, but they are sort of related to the
394  * functions just above, and they don't seem to deserve their own file.
395  *****************************************************************************/
396 
397 /*
398  * get_sortgroupref_clause
399  *		Find the SortGroupClause matching the given SortGroupRef index,
400  *		and return it.
401  */
402 SortGroupClause *
get_sortgroupref_clause(Index sortref,List * clauses)403 get_sortgroupref_clause(Index sortref, List *clauses)
404 {
405 	ListCell   *l;
406 
407 	foreach(l, clauses)
408 	{
409 		SortGroupClause *cl = (SortGroupClause *) lfirst(l);
410 
411 		if (cl->tleSortGroupRef == sortref)
412 			return cl;
413 	}
414 
415 	elog(ERROR, "ORDER/GROUP BY expression not found in list");
416 	return NULL;				/* keep compiler quiet */
417 }
418 
419 /*
420  * get_sortgroupref_clause_noerr
421  *		As above, but return NULL rather than throwing an error if not found.
422  */
423 SortGroupClause *
get_sortgroupref_clause_noerr(Index sortref,List * clauses)424 get_sortgroupref_clause_noerr(Index sortref, List *clauses)
425 {
426 	ListCell   *l;
427 
428 	foreach(l, clauses)
429 	{
430 		SortGroupClause *cl = (SortGroupClause *) lfirst(l);
431 
432 		if (cl->tleSortGroupRef == sortref)
433 			return cl;
434 	}
435 
436 	return NULL;
437 }
438 
439 /*
440  * extract_grouping_ops - make an array of the equality operator OIDs
441  *		for a SortGroupClause list
442  */
443 Oid *
extract_grouping_ops(List * groupClause)444 extract_grouping_ops(List *groupClause)
445 {
446 	int			numCols = list_length(groupClause);
447 	int			colno = 0;
448 	Oid		   *groupOperators;
449 	ListCell   *glitem;
450 
451 	groupOperators = (Oid *) palloc(sizeof(Oid) * numCols);
452 
453 	foreach(glitem, groupClause)
454 	{
455 		SortGroupClause *groupcl = (SortGroupClause *) lfirst(glitem);
456 
457 		groupOperators[colno] = groupcl->eqop;
458 		Assert(OidIsValid(groupOperators[colno]));
459 		colno++;
460 	}
461 
462 	return groupOperators;
463 }
464 
465 /*
466  * extract_grouping_cols - make an array of the grouping column resnos
467  *		for a SortGroupClause list
468  */
469 AttrNumber *
extract_grouping_cols(List * groupClause,List * tlist)470 extract_grouping_cols(List *groupClause, List *tlist)
471 {
472 	AttrNumber *grpColIdx;
473 	int			numCols = list_length(groupClause);
474 	int			colno = 0;
475 	ListCell   *glitem;
476 
477 	grpColIdx = (AttrNumber *) palloc(sizeof(AttrNumber) * numCols);
478 
479 	foreach(glitem, groupClause)
480 	{
481 		SortGroupClause *groupcl = (SortGroupClause *) lfirst(glitem);
482 		TargetEntry *tle = get_sortgroupclause_tle(groupcl, tlist);
483 
484 		grpColIdx[colno++] = tle->resno;
485 	}
486 
487 	return grpColIdx;
488 }
489 
490 /*
491  * grouping_is_sortable - is it possible to implement grouping list by sorting?
492  *
493  * This is easy since the parser will have included a sortop if one exists.
494  */
495 bool
grouping_is_sortable(List * groupClause)496 grouping_is_sortable(List *groupClause)
497 {
498 	ListCell   *glitem;
499 
500 	foreach(glitem, groupClause)
501 	{
502 		SortGroupClause *groupcl = (SortGroupClause *) lfirst(glitem);
503 
504 		if (!OidIsValid(groupcl->sortop))
505 			return false;
506 	}
507 	return true;
508 }
509 
510 /*
511  * grouping_is_hashable - is it possible to implement grouping list by hashing?
512  *
513  * We rely on the parser to have set the hashable flag correctly.
514  */
515 bool
grouping_is_hashable(List * groupClause)516 grouping_is_hashable(List *groupClause)
517 {
518 	ListCell   *glitem;
519 
520 	foreach(glitem, groupClause)
521 	{
522 		SortGroupClause *groupcl = (SortGroupClause *) lfirst(glitem);
523 
524 		if (!groupcl->hashable)
525 			return false;
526 	}
527 	return true;
528 }
529 
530 
531 /*****************************************************************************
532  *		PathTarget manipulation functions
533  *
534  * PathTarget is a somewhat stripped-down version of a full targetlist; it
535  * omits all the TargetEntry decoration except (optionally) sortgroupref data,
536  * and it adds evaluation cost and output data width info.
537  *****************************************************************************/
538 
539 /*
540  * make_pathtarget_from_tlist
541  *	  Construct a PathTarget equivalent to the given targetlist.
542  *
543  * This leaves the cost and width fields as zeroes.  Most callers will want
544  * to use create_pathtarget(), so as to get those set.
545  */
546 PathTarget *
make_pathtarget_from_tlist(List * tlist)547 make_pathtarget_from_tlist(List *tlist)
548 {
549 	PathTarget *target = makeNode(PathTarget);
550 	int			i;
551 	ListCell   *lc;
552 
553 	target->sortgrouprefs = (Index *) palloc(list_length(tlist) * sizeof(Index));
554 
555 	i = 0;
556 	foreach(lc, tlist)
557 	{
558 		TargetEntry *tle = (TargetEntry *) lfirst(lc);
559 
560 		target->exprs = lappend(target->exprs, tle->expr);
561 		target->sortgrouprefs[i] = tle->ressortgroupref;
562 		i++;
563 	}
564 
565 	return target;
566 }
567 
568 /*
569  * make_tlist_from_pathtarget
570  *	  Construct a targetlist from a PathTarget.
571  */
572 List *
make_tlist_from_pathtarget(PathTarget * target)573 make_tlist_from_pathtarget(PathTarget *target)
574 {
575 	List	   *tlist = NIL;
576 	int			i;
577 	ListCell   *lc;
578 
579 	i = 0;
580 	foreach(lc, target->exprs)
581 	{
582 		Expr	   *expr = (Expr *) lfirst(lc);
583 		TargetEntry *tle;
584 
585 		tle = makeTargetEntry(expr,
586 							  i + 1,
587 							  NULL,
588 							  false);
589 		if (target->sortgrouprefs)
590 			tle->ressortgroupref = target->sortgrouprefs[i];
591 		tlist = lappend(tlist, tle);
592 		i++;
593 	}
594 
595 	return tlist;
596 }
597 
598 /*
599  * copy_pathtarget
600  *	  Copy a PathTarget.
601  *
602  * The new PathTarget has its own List cells, but shares the underlying
603  * target expression trees with the old one.  We duplicate the List cells
604  * so that items can be added to one target without damaging the other.
605  */
606 PathTarget *
copy_pathtarget(PathTarget * src)607 copy_pathtarget(PathTarget *src)
608 {
609 	PathTarget *dst = makeNode(PathTarget);
610 
611 	/* Copy scalar fields */
612 	memcpy(dst, src, sizeof(PathTarget));
613 	/* Shallow-copy the expression list */
614 	dst->exprs = list_copy(src->exprs);
615 	/* Duplicate sortgrouprefs if any (if not, the memcpy handled this) */
616 	if (src->sortgrouprefs)
617 	{
618 		Size		nbytes = list_length(src->exprs) * sizeof(Index);
619 
620 		dst->sortgrouprefs = (Index *) palloc(nbytes);
621 		memcpy(dst->sortgrouprefs, src->sortgrouprefs, nbytes);
622 	}
623 	return dst;
624 }
625 
626 /*
627  * create_empty_pathtarget
628  *	  Create an empty (zero columns, zero cost) PathTarget.
629  */
630 PathTarget *
create_empty_pathtarget(void)631 create_empty_pathtarget(void)
632 {
633 	/* This is easy, but we don't want callers to hard-wire this ... */
634 	return makeNode(PathTarget);
635 }
636 
637 /*
638  * add_column_to_pathtarget
639  *		Append a target column to the PathTarget.
640  *
641  * As with make_pathtarget_from_tlist, we leave it to the caller to update
642  * the cost and width fields.
643  */
644 void
add_column_to_pathtarget(PathTarget * target,Expr * expr,Index sortgroupref)645 add_column_to_pathtarget(PathTarget *target, Expr *expr, Index sortgroupref)
646 {
647 	/* Updating the exprs list is easy ... */
648 	target->exprs = lappend(target->exprs, expr);
649 	/* ... the sortgroupref data, a bit less so */
650 	if (target->sortgrouprefs)
651 	{
652 		int			nexprs = list_length(target->exprs);
653 
654 		/* This might look inefficient, but actually it's usually cheap */
655 		target->sortgrouprefs = (Index *)
656 			repalloc(target->sortgrouprefs, nexprs * sizeof(Index));
657 		target->sortgrouprefs[nexprs - 1] = sortgroupref;
658 	}
659 	else if (sortgroupref)
660 	{
661 		/* Adding sortgroupref labeling to a previously unlabeled target */
662 		int			nexprs = list_length(target->exprs);
663 
664 		target->sortgrouprefs = (Index *) palloc0(nexprs * sizeof(Index));
665 		target->sortgrouprefs[nexprs - 1] = sortgroupref;
666 	}
667 }
668 
669 /*
670  * add_new_column_to_pathtarget
671  *		Append a target column to the PathTarget, but only if it's not
672  *		equal() to any pre-existing target expression.
673  *
674  * The caller cannot specify a sortgroupref, since it would be unclear how
675  * to merge that with a pre-existing column.
676  *
677  * As with make_pathtarget_from_tlist, we leave it to the caller to update
678  * the cost and width fields.
679  */
680 void
add_new_column_to_pathtarget(PathTarget * target,Expr * expr)681 add_new_column_to_pathtarget(PathTarget *target, Expr *expr)
682 {
683 	if (!list_member(target->exprs, expr))
684 		add_column_to_pathtarget(target, expr, 0);
685 }
686 
687 /*
688  * add_new_columns_to_pathtarget
689  *		Apply add_new_column_to_pathtarget() for each element of the list.
690  */
691 void
add_new_columns_to_pathtarget(PathTarget * target,List * exprs)692 add_new_columns_to_pathtarget(PathTarget *target, List *exprs)
693 {
694 	ListCell   *lc;
695 
696 	foreach(lc, exprs)
697 	{
698 		Expr	   *expr = (Expr *) lfirst(lc);
699 
700 		add_new_column_to_pathtarget(target, expr);
701 	}
702 }
703 
704 /*
705  * apply_pathtarget_labeling_to_tlist
706  *		Apply any sortgrouprefs in the PathTarget to matching tlist entries
707  *
708  * Here, we do not assume that the tlist entries are one-for-one with the
709  * PathTarget.  The intended use of this function is to deal with cases
710  * where createplan.c has decided to use some other tlist and we have
711  * to identify what matches exist.
712  */
713 void
apply_pathtarget_labeling_to_tlist(List * tlist,PathTarget * target)714 apply_pathtarget_labeling_to_tlist(List *tlist, PathTarget *target)
715 {
716 	int			i;
717 	ListCell   *lc;
718 
719 	/* Nothing to do if PathTarget has no sortgrouprefs data */
720 	if (target->sortgrouprefs == NULL)
721 		return;
722 
723 	i = 0;
724 	foreach(lc, target->exprs)
725 	{
726 		Expr	   *expr = (Expr *) lfirst(lc);
727 		TargetEntry *tle;
728 
729 		if (target->sortgrouprefs[i])
730 		{
731 			/*
732 			 * For Vars, use tlist_member_match_var's weakened matching rule;
733 			 * this allows us to deal with some cases where a set-returning
734 			 * function has been inlined, so that we now have more knowledge
735 			 * about what it returns than we did when the original Var was
736 			 * created.  Otherwise, use regular equal() to find the matching
737 			 * TLE.  (In current usage, only the Var case is actually needed;
738 			 * but it seems best to have sane behavior here for non-Vars too.)
739 			 */
740 			if (expr && IsA(expr, Var))
741 				tle = tlist_member_match_var((Var *) expr, tlist);
742 			else
743 				tle = tlist_member((Node *) expr, tlist);
744 
745 			/*
746 			 * Complain if noplace for the sortgrouprefs label, or if we'd
747 			 * have to label a column twice.  (The case where it already has
748 			 * the desired label probably can't happen, but we may as well
749 			 * allow for it.)
750 			 */
751 			if (!tle)
752 				elog(ERROR, "ORDER/GROUP BY expression not found in targetlist");
753 			if (tle->ressortgroupref != 0 &&
754 				tle->ressortgroupref != target->sortgrouprefs[i])
755 				elog(ERROR, "targetlist item has multiple sortgroupref labels");
756 
757 			tle->ressortgroupref = target->sortgrouprefs[i];
758 		}
759 		i++;
760 	}
761 }
762