1996-07-09 08:22:35 +02:00
|
|
|
/*-------------------------------------------------------------------------
|
|
|
|
*
|
1999-02-14 00:22:53 +01:00
|
|
|
* tlist.c
|
1997-09-07 07:04:48 +02:00
|
|
|
* Target list manipulation routines
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
|
|
|
* Copyright (c) 1994, Regents of the University of California
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* IDENTIFICATION
|
1999-08-21 05:49:17 +02:00
|
|
|
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/tlist.c,v 1.39 1999/08/21 03:49:07 tgl Exp $
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
#include "postgres.h"
|
|
|
|
|
|
|
|
#include "nodes/makefuncs.h"
|
1999-07-16 07:00:38 +02:00
|
|
|
#include "nodes/nodeFuncs.h"
|
|
|
|
#include "optimizer/clauses.h"
|
|
|
|
#include "optimizer/tlist.h"
|
|
|
|
#include "optimizer/var.h"
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1999-08-21 05:49:17 +02:00
|
|
|
static Node *unflatten_tlist_mutator(Node *node, List *flat_tlist);
|
1996-07-09 08:22:35 +02:00
|
|
|
|
|
|
|
/*****************************************************************************
|
1997-09-07 07:04:48 +02:00
|
|
|
* ---------- RELATION node target list routines ----------
|
1996-07-09 08:22:35 +02:00
|
|
|
*****************************************************************************/
|
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
/*
|
1999-02-14 00:22:53 +01:00
|
|
|
* tlistentry_member
|
1999-08-09 07:34:13 +02:00
|
|
|
* Finds the (first) member of the given tlist whose expression is
|
|
|
|
* var_equal() to the given var. Result is NULL if no such member.
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
1997-09-08 04:41:22 +02:00
|
|
|
TargetEntry *
|
1997-09-08 23:56:23 +02:00
|
|
|
tlistentry_member(Var *var, List *targetlist)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1999-08-09 07:34:13 +02:00
|
|
|
if (var && IsA(var, Var))
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
1999-05-07 01:07:33 +02:00
|
|
|
List *temp;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
foreach(temp, targetlist)
|
|
|
|
{
|
1999-08-09 07:34:13 +02:00
|
|
|
if (var_equal(var, get_expr(lfirst(temp))))
|
1998-09-01 05:29:17 +02:00
|
|
|
return (TargetEntry *) lfirst(temp);
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
1998-09-01 05:29:17 +02:00
|
|
|
return NULL;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
/*
|
1999-02-15 06:56:07 +01:00
|
|
|
* matching_tlist_var
|
1999-08-09 07:34:13 +02:00
|
|
|
* Same as tlistentry_member(), except returns the tlist expression
|
|
|
|
* rather than its parent TargetEntry node.
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
1998-02-26 05:46:47 +01:00
|
|
|
Expr *
|
1999-02-15 06:56:07 +01:00
|
|
|
matching_tlist_var(Var *var, List *targetlist)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
TargetEntry *tlentry;
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
tlentry = tlistentry_member(var, targetlist);
|
|
|
|
if (tlentry)
|
1998-09-01 05:29:17 +02:00
|
|
|
return (Expr *) get_expr(tlentry);
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1998-09-01 05:29:17 +02:00
|
|
|
return (Expr *) NULL;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
1999-08-09 07:34:13 +02:00
|
|
|
/*
|
|
|
|
* tlist_member
|
|
|
|
* Same as tlistentry_member(), except returns the Resdom node
|
|
|
|
* rather than its parent TargetEntry node.
|
|
|
|
*/
|
|
|
|
Resdom *
|
|
|
|
tlist_member(Var *var, List *tlist)
|
|
|
|
{
|
|
|
|
TargetEntry *tlentry;
|
|
|
|
|
|
|
|
tlentry = tlistentry_member(var, tlist);
|
|
|
|
if (tlentry)
|
|
|
|
return tlentry->resdom;
|
|
|
|
|
|
|
|
return (Resdom *) NULL;
|
|
|
|
}
|
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
/*
|
1999-02-14 00:22:53 +01:00
|
|
|
* add_var_to_tlist
|
1997-09-07 07:04:48 +02:00
|
|
|
* Creates a targetlist entry corresponding to the supplied var node
|
1999-08-09 07:34:13 +02:00
|
|
|
* 'var' and adds the new targetlist entry to the targetlist field of
|
|
|
|
* 'rel'. No entry is created if 'var' is already in the tlist.
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
|
|
|
void
|
1999-05-26 00:43:53 +02:00
|
|
|
add_var_to_tlist(RelOptInfo *rel, Var *var)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1999-08-21 05:49:17 +02:00
|
|
|
if (! tlistentry_member(var, rel->targetlist))
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
1999-08-09 07:34:13 +02:00
|
|
|
/* XXX is copyObject necessary here? */
|
|
|
|
rel->targetlist = lappend(rel->targetlist,
|
|
|
|
create_tl_element((Var *) copyObject(var),
|
|
|
|
length(rel->targetlist) + 1));
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
/*
|
1999-02-14 00:22:53 +01:00
|
|
|
* create_tl_element
|
1997-09-07 07:04:48 +02:00
|
|
|
* Creates a target list entry node and its associated (resdom var) pair
|
1999-08-09 07:34:13 +02:00
|
|
|
* with its resdom number equal to 'resdomno'.
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
1997-09-08 04:41:22 +02:00
|
|
|
TargetEntry *
|
1997-09-08 23:56:23 +02:00
|
|
|
create_tl_element(Var *var, int resdomno)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1998-09-01 06:40:42 +02:00
|
|
|
return makeTargetEntry(makeResdom(resdomno,
|
|
|
|
var->vartype,
|
|
|
|
var->vartypmod,
|
|
|
|
NULL,
|
|
|
|
(Index) 0,
|
|
|
|
(Oid) 0,
|
1999-05-17 19:03:51 +02:00
|
|
|
false),
|
1998-09-01 06:40:42 +02:00
|
|
|
(Node *) var);
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
/*
|
1999-02-14 00:22:53 +01:00
|
|
|
* get_actual_tlist
|
1997-09-07 07:04:48 +02:00
|
|
|
* Returns the targetlist elements from a relation tlist.
|
|
|
|
*
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
1998-02-26 05:46:47 +01:00
|
|
|
List *
|
1997-09-08 23:56:23 +02:00
|
|
|
get_actual_tlist(List *tlist)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* this function is not making sense. - ay 10/94
|
|
|
|
*/
|
1999-02-21 04:49:55 +01:00
|
|
|
#ifdef NOT_USED
|
1997-09-08 04:41:22 +02:00
|
|
|
List *element = NIL;
|
|
|
|
List *result = NIL;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
if (tlist == NULL)
|
|
|
|
{
|
|
|
|
elog(DEBUG, "calling get_actual_tlist with empty tlist");
|
1998-09-01 05:29:17 +02:00
|
|
|
return NIL;
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* XXX - it is unclear to me what exactly get_entry should be doing,
|
|
|
|
* as it is unclear to me the exact relationship between "TL" "TLE"
|
|
|
|
* and joinlists
|
|
|
|
*/
|
|
|
|
|
|
|
|
foreach(element, tlist)
|
|
|
|
result = lappend(result, lfirst((List *) lfirst(element)));
|
|
|
|
|
1998-09-01 05:29:17 +02:00
|
|
|
return result;
|
1996-07-09 08:22:35 +02:00
|
|
|
#endif
|
1997-09-07 07:04:48 +02:00
|
|
|
return tlist;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************
|
1997-09-07 07:04:48 +02:00
|
|
|
* ---------- GENERAL target list routines ----------
|
1996-07-09 08:22:35 +02:00
|
|
|
*****************************************************************************/
|
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
/*
|
1999-02-14 00:22:53 +01:00
|
|
|
* match_varid
|
1999-08-09 07:34:13 +02:00
|
|
|
* Searches a target list for an entry matching a given var.
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
1999-08-09 07:34:13 +02:00
|
|
|
* Returns the target list entry (resdom var) of the matching var,
|
|
|
|
* or NULL if no match.
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
1997-09-08 04:41:22 +02:00
|
|
|
TargetEntry *
|
1997-09-08 23:56:23 +02:00
|
|
|
match_varid(Var *test_var, List *tlist)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
List *tl;
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1999-08-09 07:34:13 +02:00
|
|
|
Assert(test_var->varlevelsup == 0); /* XXX why? */
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
foreach(tl, tlist)
|
|
|
|
{
|
1999-08-09 07:34:13 +02:00
|
|
|
TargetEntry *entry = lfirst(tl);
|
|
|
|
Var *tlvar = get_expr(entry);
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
if (!IsA(tlvar, Var))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/*
|
1999-08-09 07:34:13 +02:00
|
|
|
* we test the original varno, instead of varno which might be
|
|
|
|
* changed to INNER/OUTER. XXX is test on vartype necessary?
|
1997-09-07 07:04:48 +02:00
|
|
|
*/
|
1998-02-26 05:46:47 +01:00
|
|
|
Assert(tlvar->varlevelsup == 0);
|
1999-08-09 07:34:13 +02:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
if (tlvar->varnoold == test_var->varnoold &&
|
1999-08-09 07:34:13 +02:00
|
|
|
tlvar->varoattno == test_var->varoattno &&
|
|
|
|
tlvar->vartype == test_var->vartype)
|
|
|
|
return entry;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
1998-09-01 05:29:17 +02:00
|
|
|
return NULL;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
/*
|
1999-02-14 00:22:53 +01:00
|
|
|
* new_unsorted_tlist
|
1997-09-07 07:04:48 +02:00
|
|
|
* Creates a copy of a target list by creating new resdom nodes
|
|
|
|
* without sort information.
|
|
|
|
*
|
1996-07-09 08:22:35 +02:00
|
|
|
* 'targetlist' is the target list to be copied.
|
1997-09-07 07:04:48 +02:00
|
|
|
*
|
1996-07-09 08:22:35 +02:00
|
|
|
* Returns the resulting target list.
|
1997-09-07 07:04:48 +02:00
|
|
|
*
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
1998-02-26 05:46:47 +01:00
|
|
|
List *
|
1997-09-08 23:56:23 +02:00
|
|
|
new_unsorted_tlist(List *targetlist)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
List *new_targetlist = (List *) copyObject((Node *) targetlist);
|
1999-05-07 01:07:33 +02:00
|
|
|
List *x;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
foreach(x, new_targetlist)
|
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
TargetEntry *tle = (TargetEntry *) lfirst(x);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
tle->resdom->reskey = 0;
|
|
|
|
tle->resdom->reskeyop = (Oid) 0;
|
|
|
|
}
|
1998-09-01 05:29:17 +02:00
|
|
|
return new_targetlist;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
/*
|
1999-02-14 00:22:53 +01:00
|
|
|
* copy_vars
|
1997-09-07 07:04:48 +02:00
|
|
|
* Replaces the var nodes in the first target list with those from
|
|
|
|
* the second target list. The two target lists are assumed to be
|
|
|
|
* identical except their actual resdoms and vars are different.
|
|
|
|
*
|
1996-07-09 08:22:35 +02:00
|
|
|
* 'target' is the target list to be replaced
|
|
|
|
* 'source' is the target list to be copied
|
1997-09-07 07:04:48 +02:00
|
|
|
*
|
1996-07-09 08:22:35 +02:00
|
|
|
* Returns a new target list.
|
1997-09-07 07:04:48 +02:00
|
|
|
*
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
1998-02-26 05:46:47 +01:00
|
|
|
List *
|
1997-09-08 23:56:23 +02:00
|
|
|
copy_vars(List *target, List *source)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
List *result = NIL;
|
1999-08-09 07:34:13 +02:00
|
|
|
List *src;
|
|
|
|
List *dest;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1999-08-09 07:34:13 +02:00
|
|
|
for (src = source, dest = target;
|
|
|
|
src != NIL && dest != NIL;
|
|
|
|
src = lnext(src), dest = lnext(dest))
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
1998-07-20 21:53:53 +02:00
|
|
|
TargetEntry *temp = makeTargetEntry(((TargetEntry *) lfirst(dest))->resdom,
|
1998-09-01 06:40:42 +02:00
|
|
|
(Node *) get_expr(lfirst(src)));
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
result = lappend(result, temp);
|
|
|
|
}
|
1998-09-01 05:29:17 +02:00
|
|
|
return result;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
/*
|
1999-02-14 00:22:53 +01:00
|
|
|
* flatten_tlist
|
1997-09-07 07:04:48 +02:00
|
|
|
* Create a target list that only contains unique variables.
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
|
|
|
* 'tlist' is the current target list
|
1997-09-07 07:04:48 +02:00
|
|
|
*
|
1996-07-09 08:22:35 +02:00
|
|
|
* Returns the "flattened" new target list.
|
1997-09-07 07:04:48 +02:00
|
|
|
*
|
1999-08-21 05:49:17 +02:00
|
|
|
* The result is entirely new structure sharing no nodes with the original.
|
|
|
|
* Copying the Var nodes is probably overkill, but be safe for now.
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
1998-02-26 05:46:47 +01:00
|
|
|
List *
|
1997-09-08 23:56:23 +02:00
|
|
|
flatten_tlist(List *tlist)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1999-08-10 05:00:15 +02:00
|
|
|
List *vlist = pull_var_clause((Node *) tlist);
|
1999-08-21 05:49:17 +02:00
|
|
|
List *new_tlist;
|
|
|
|
|
|
|
|
new_tlist = add_to_flat_tlist(NIL, vlist);
|
|
|
|
freeList(vlist);
|
|
|
|
return new_tlist;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* add_to_flat_tlist
|
|
|
|
* Add more vars to a flattened tlist (if they're not already in it)
|
|
|
|
*
|
|
|
|
* 'tlist' is the flattened tlist
|
|
|
|
* 'vars' is a list of var nodes
|
|
|
|
*
|
|
|
|
* Returns the extended tlist.
|
|
|
|
*/
|
|
|
|
List *
|
|
|
|
add_to_flat_tlist(List *tlist, List *vars)
|
|
|
|
{
|
|
|
|
int next_resdomno = length(tlist) + 1;
|
1999-08-10 05:00:15 +02:00
|
|
|
List *v;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1999-08-21 05:49:17 +02:00
|
|
|
foreach(v, vars)
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
1999-08-10 05:00:15 +02:00
|
|
|
Var *var = lfirst(v);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1999-08-21 05:49:17 +02:00
|
|
|
if (! tlistentry_member(var, tlist))
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
1999-08-10 05:00:15 +02:00
|
|
|
Resdom *r;
|
|
|
|
|
1999-08-21 05:49:17 +02:00
|
|
|
r = makeResdom(next_resdomno++,
|
1999-08-10 05:00:15 +02:00
|
|
|
var->vartype,
|
|
|
|
var->vartypmod,
|
|
|
|
NULL,
|
|
|
|
(Index) 0,
|
|
|
|
(Oid) 0,
|
|
|
|
false);
|
1999-08-21 05:49:17 +02:00
|
|
|
tlist = lappend(tlist,
|
|
|
|
makeTargetEntry(r, copyObject(var)));
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
1999-08-21 05:49:17 +02:00
|
|
|
return tlist;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
/*
|
1999-08-21 05:49:17 +02:00
|
|
|
* unflatten_tlist
|
|
|
|
* Reconstructs the target list of a query by replacing vars within
|
1999-08-09 07:34:13 +02:00
|
|
|
* target expressions with vars from the 'flattened' target list.
|
1997-09-07 07:04:48 +02:00
|
|
|
*
|
1999-08-21 05:49:17 +02:00
|
|
|
* XXX is this really necessary? Why can't we just use the tlist as is?
|
|
|
|
*
|
1999-08-09 07:34:13 +02:00
|
|
|
* 'full_tlist' is the original target list
|
1999-02-14 00:22:53 +01:00
|
|
|
* 'flat_tlist' is the flattened (var-only) target list
|
1997-09-07 07:04:48 +02:00
|
|
|
*
|
1999-08-09 07:34:13 +02:00
|
|
|
* Returns the rebuilt target list. The original is not modified.
|
1997-09-07 07:04:48 +02:00
|
|
|
*
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
1998-02-26 05:46:47 +01:00
|
|
|
List *
|
1999-08-21 05:49:17 +02:00
|
|
|
unflatten_tlist(List *full_tlist, List *flat_tlist)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1999-08-21 05:49:17 +02:00
|
|
|
return (List *) unflatten_tlist_mutator((Node *) full_tlist,
|
|
|
|
flat_tlist);
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
1997-09-08 04:41:22 +02:00
|
|
|
static Node *
|
1999-08-21 05:49:17 +02:00
|
|
|
unflatten_tlist_mutator(Node *node, List *flat_tlist)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1999-08-09 07:34:13 +02:00
|
|
|
if (node == NULL)
|
1997-09-07 07:04:48 +02:00
|
|
|
return NULL;
|
1999-08-09 07:34:13 +02:00
|
|
|
if (IsA(node, Var))
|
|
|
|
return (Node *) get_expr(match_varid((Var *) node,
|
1999-05-07 01:07:33 +02:00
|
|
|
flat_tlist));
|
1999-08-21 05:49:17 +02:00
|
|
|
return expression_tree_mutator(node, unflatten_tlist_mutator,
|
1999-08-09 07:34:13 +02:00
|
|
|
(void *) flat_tlist);
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
1998-02-26 05:46:47 +01:00
|
|
|
Var *
|
1997-09-08 23:56:23 +02:00
|
|
|
get_expr(TargetEntry *tle)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-07 07:04:48 +02:00
|
|
|
Assert(tle != NULL);
|
|
|
|
Assert(tle->expr != NULL);
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1998-09-01 05:29:17 +02:00
|
|
|
return (Var *) tle->expr;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
1999-08-21 05:49:17 +02:00
|
|
|
/*
|
|
|
|
* get_sortgroupclause_expr
|
|
|
|
* Find the targetlist entry matching the given SortClause
|
|
|
|
* (or GroupClause) by ressortgroupref, and return its expression.
|
|
|
|
*
|
|
|
|
* Because GroupClause is typedef'd as SortClause, either kind of
|
|
|
|
* node can be passed without casting.
|
|
|
|
*/
|
|
|
|
Node *
|
|
|
|
get_sortgroupclause_expr(SortClause *sortClause, List *targetList)
|
1999-05-12 17:02:39 +02:00
|
|
|
{
|
1999-08-21 05:49:17 +02:00
|
|
|
Index refnumber = sortClause->tleSortGroupRef;
|
1999-05-25 18:15:34 +02:00
|
|
|
List *l;
|
1999-05-12 17:02:39 +02:00
|
|
|
|
|
|
|
foreach(l, targetList)
|
|
|
|
{
|
1999-08-09 07:34:13 +02:00
|
|
|
TargetEntry *tle = (TargetEntry *) lfirst(l);
|
1999-08-21 05:49:17 +02:00
|
|
|
|
|
|
|
if (tle->resdom->ressortgroupref == refnumber)
|
|
|
|
return tle->expr;
|
1999-05-12 17:02:39 +02:00
|
|
|
}
|
|
|
|
|
1999-08-21 05:49:17 +02:00
|
|
|
elog(ERROR, "get_sortgroupclause_expr: ORDER/GROUP BY expression not found in targetlist");
|
|
|
|
return NULL; /* keep compiler quiet */
|
1999-05-12 17:02:39 +02:00
|
|
|
}
|