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
|
|
|
*
|
2015-01-06 17:43:47 +01:00
|
|
|
* Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group
|
2000-01-26 06:58:53 +01:00
|
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
|
|
|
*
|
|
|
|
* IDENTIFICATION
|
2010-09-20 22:08:53 +02:00
|
|
|
* src/backend/optimizer/util/tlist.c
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
#include "postgres.h"
|
|
|
|
|
|
|
|
#include "nodes/makefuncs.h"
|
2008-08-26 00:42:34 +02:00
|
|
|
#include "nodes/nodeFuncs.h"
|
1999-07-16 07:00:38 +02:00
|
|
|
#include "optimizer/tlist.h"
|
1996-07-09 08:22:35 +02:00
|
|
|
|
2000-06-09 00:38:00 +02:00
|
|
|
|
1996-07-09 08:22:35 +02:00
|
|
|
/*****************************************************************************
|
2003-06-30 01:05:05 +02:00
|
|
|
* Target list creation and searching utilities
|
1996-07-09 08:22:35 +02:00
|
|
|
*****************************************************************************/
|
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
/*
|
2005-04-06 18:34:07 +02:00
|
|
|
* tlist_member
|
1999-08-09 07:34:13 +02:00
|
|
|
* Finds the (first) member of the given tlist whose expression is
|
2014-05-06 18:12:18 +02:00
|
|
|
* equal() to the given expression. Result is NULL if no such member.
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
2001-01-09 04:48:51 +01:00
|
|
|
TargetEntry *
|
2005-04-06 18:34:07 +02:00
|
|
|
tlist_member(Node *node, List *targetlist)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
2004-05-26 06:41:50 +02:00
|
|
|
ListCell *temp;
|
1999-08-22 22:15:04 +02:00
|
|
|
|
|
|
|
foreach(temp, targetlist)
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
2000-04-12 19:17:23 +02:00
|
|
|
TargetEntry *tlentry = (TargetEntry *) lfirst(temp);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1999-08-22 22:15:04 +02:00
|
|
|
if (equal(node, tlentry->expr))
|
|
|
|
return tlentry;
|
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
|
|
|
}
|
|
|
|
|
2007-11-08 20:25:37 +01:00
|
|
|
/*
|
|
|
|
* tlist_member_ignore_relabel
|
|
|
|
* Same as above, except that we ignore top-level RelabelType nodes
|
|
|
|
* while checking for a match. This is needed for some scenarios
|
|
|
|
* involving binary-compatible sort operations.
|
|
|
|
*/
|
|
|
|
TargetEntry *
|
|
|
|
tlist_member_ignore_relabel(Node *node, List *targetlist)
|
|
|
|
{
|
|
|
|
ListCell *temp;
|
|
|
|
|
|
|
|
while (node && IsA(node, RelabelType))
|
|
|
|
node = (Node *) ((RelabelType *) node)->arg;
|
|
|
|
|
|
|
|
foreach(temp, targetlist)
|
|
|
|
{
|
|
|
|
TargetEntry *tlentry = (TargetEntry *) lfirst(temp);
|
2007-11-15 22:14:46 +01:00
|
|
|
Expr *tlexpr = tlentry->expr;
|
2007-11-08 20:25:37 +01:00
|
|
|
|
|
|
|
while (tlexpr && IsA(tlexpr, RelabelType))
|
|
|
|
tlexpr = ((RelabelType *) tlexpr)->arg;
|
|
|
|
|
|
|
|
if (equal(node, tlexpr))
|
|
|
|
return tlentry;
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2013-08-23 23:30:53 +02:00
|
|
|
/*
|
|
|
|
* tlist_member_match_var
|
|
|
|
* Same as above, except that we match the provided Var on the basis
|
|
|
|
* of varno/varattno/varlevelsup only, rather than using full equal().
|
|
|
|
*
|
|
|
|
* This is needed in some cases where we can't be sure of an exact typmod
|
|
|
|
* match. It's probably a good idea to check the vartype anyway, but
|
|
|
|
* we leave it to the caller to apply any suitable sanity checks.
|
|
|
|
*/
|
|
|
|
TargetEntry *
|
|
|
|
tlist_member_match_var(Var *var, List *targetlist)
|
|
|
|
{
|
|
|
|
ListCell *temp;
|
|
|
|
|
|
|
|
foreach(temp, targetlist)
|
|
|
|
{
|
|
|
|
TargetEntry *tlentry = (TargetEntry *) lfirst(temp);
|
|
|
|
Var *tlvar = (Var *) tlentry->expr;
|
|
|
|
|
|
|
|
if (!tlvar || !IsA(tlvar, Var))
|
|
|
|
continue;
|
|
|
|
if (var->varno == tlvar->varno &&
|
|
|
|
var->varattno == tlvar->varattno &&
|
|
|
|
var->varlevelsup == tlvar->varlevelsup)
|
|
|
|
return tlentry;
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
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
|
|
|
*
|
Avoid listing ungrouped Vars in the targetlist of Agg-underneath-Window.
Regular aggregate functions in combination with, or within the arguments
of, window functions are OK per spec; they have the semantics that the
aggregate output rows are computed and then we run the window functions
over that row set. (Thus, this combination is not really useful unless
there's a GROUP BY so that more than one aggregate output row is possible.)
The case without GROUP BY could fail, as recently reported by Jeff Davis,
because sloppy construction of the Agg node's targetlist resulted in extra
references to possibly-ungrouped Vars appearing outside the aggregate
function calls themselves. See the added regression test case for an
example.
Fixing this requires modifying the API of flatten_tlist and its underlying
function pull_var_clause. I chose to make pull_var_clause's API for
aggregates identical to what it was already doing for placeholders, since
the useful behaviors turn out to be the same (error, report node as-is, or
recurse into it). I also tightened the error checking in this area a bit:
if it was ever valid to see an uplevel Var, Aggref, or PlaceHolderVar here,
that was a long time ago, so complain instead of ignoring them.
Backpatch into 9.1. The failure exists in 8.4 and 9.0 as well, but seeing
that it only occurs in a basically-useless corner case, it doesn't seem
worth the risks of changing a function API in a minor release. There might
be third-party code using pull_var_clause.
2011-07-13 00:23:55 +02:00
|
|
|
* Aggrefs and PlaceHolderVars in the input are treated according to
|
|
|
|
* aggbehavior and phbehavior, for which see pull_var_clause().
|
1999-08-26 07:09:06 +02:00
|
|
|
*
|
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 *
|
Avoid listing ungrouped Vars in the targetlist of Agg-underneath-Window.
Regular aggregate functions in combination with, or within the arguments
of, window functions are OK per spec; they have the semantics that the
aggregate output rows are computed and then we run the window functions
over that row set. (Thus, this combination is not really useful unless
there's a GROUP BY so that more than one aggregate output row is possible.)
The case without GROUP BY could fail, as recently reported by Jeff Davis,
because sloppy construction of the Agg node's targetlist resulted in extra
references to possibly-ungrouped Vars appearing outside the aggregate
function calls themselves. See the added regression test case for an
example.
Fixing this requires modifying the API of flatten_tlist and its underlying
function pull_var_clause. I chose to make pull_var_clause's API for
aggregates identical to what it was already doing for placeholders, since
the useful behaviors turn out to be the same (error, report node as-is, or
recurse into it). I also tightened the error checking in this area a bit:
if it was ever valid to see an uplevel Var, Aggref, or PlaceHolderVar here,
that was a long time ago, so complain instead of ignoring them.
Backpatch into 9.1. The failure exists in 8.4 and 9.0 as well, but seeing
that it only occurs in a basically-useless corner case, it doesn't seem
worth the risks of changing a function API in a minor release. There might
be third-party code using pull_var_clause.
2011-07-13 00:23:55 +02:00
|
|
|
flatten_tlist(List *tlist, PVCAggregateBehavior aggbehavior,
|
|
|
|
PVCPlaceHolderBehavior phbehavior)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
2009-04-19 21:46:33 +02:00
|
|
|
List *vlist = pull_var_clause((Node *) tlist,
|
Avoid listing ungrouped Vars in the targetlist of Agg-underneath-Window.
Regular aggregate functions in combination with, or within the arguments
of, window functions are OK per spec; they have the semantics that the
aggregate output rows are computed and then we run the window functions
over that row set. (Thus, this combination is not really useful unless
there's a GROUP BY so that more than one aggregate output row is possible.)
The case without GROUP BY could fail, as recently reported by Jeff Davis,
because sloppy construction of the Agg node's targetlist resulted in extra
references to possibly-ungrouped Vars appearing outside the aggregate
function calls themselves. See the added regression test case for an
example.
Fixing this requires modifying the API of flatten_tlist and its underlying
function pull_var_clause. I chose to make pull_var_clause's API for
aggregates identical to what it was already doing for placeholders, since
the useful behaviors turn out to be the same (error, report node as-is, or
recurse into it). I also tightened the error checking in this area a bit:
if it was ever valid to see an uplevel Var, Aggref, or PlaceHolderVar here,
that was a long time ago, so complain instead of ignoring them.
Backpatch into 9.1. The failure exists in 8.4 and 9.0 as well, but seeing
that it only occurs in a basically-useless corner case, it doesn't seem
worth the risks of changing a function API in a minor release. There might
be third-party code using pull_var_clause.
2011-07-13 00:23:55 +02:00
|
|
|
aggbehavior,
|
|
|
|
phbehavior);
|
1999-08-21 05:49:17 +02:00
|
|
|
List *new_tlist;
|
|
|
|
|
|
|
|
new_tlist = add_to_flat_tlist(NIL, vlist);
|
2004-05-31 01:40:41 +02:00
|
|
|
list_free(vlist);
|
1999-08-21 05:49:17 +02:00
|
|
|
return new_tlist;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* add_to_flat_tlist
|
2008-12-28 19:54:01 +01:00
|
|
|
* Add more items to a flattened tlist (if they're not already in it)
|
1999-08-21 05:49:17 +02:00
|
|
|
*
|
|
|
|
* 'tlist' is the flattened tlist
|
2008-12-28 19:54:01 +01:00
|
|
|
* 'exprs' is a list of expressions (usually, but not necessarily, Vars)
|
1999-08-21 05:49:17 +02:00
|
|
|
*
|
|
|
|
* Returns the extended tlist.
|
|
|
|
*/
|
|
|
|
List *
|
2008-12-28 19:54:01 +01:00
|
|
|
add_to_flat_tlist(List *tlist, List *exprs)
|
1999-08-21 05:49:17 +02:00
|
|
|
{
|
2005-04-06 18:34:07 +02:00
|
|
|
int next_resno = list_length(tlist) + 1;
|
2008-12-28 19:54:01 +01:00
|
|
|
ListCell *lc;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2008-12-28 19:54:01 +01:00
|
|
|
foreach(lc, exprs)
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
2008-12-28 19:54:01 +01:00
|
|
|
Node *expr = (Node *) lfirst(lc);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2008-12-28 19:54:01 +01:00
|
|
|
if (!tlist_member(expr, tlist))
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
2005-04-06 18:34:07 +02:00
|
|
|
TargetEntry *tle;
|
|
|
|
|
2008-12-28 19:54:01 +01:00
|
|
|
tle = makeTargetEntry(copyObject(expr), /* copy needed?? */
|
2005-04-06 18:34:07 +02:00
|
|
|
next_resno++,
|
|
|
|
NULL,
|
|
|
|
false);
|
|
|
|
tlist = lappend(tlist, tle);
|
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
|
|
|
}
|
|
|
|
|
2007-11-08 22:49:48 +01:00
|
|
|
|
2008-08-07 21:35:02 +02:00
|
|
|
/*
|
|
|
|
* get_tlist_exprs
|
|
|
|
* Get just the expression subtrees of a tlist
|
|
|
|
*
|
|
|
|
* Resjunk columns are ignored unless includeJunk is true
|
|
|
|
*/
|
|
|
|
List *
|
|
|
|
get_tlist_exprs(List *tlist, bool includeJunk)
|
|
|
|
{
|
|
|
|
List *result = NIL;
|
|
|
|
ListCell *l;
|
|
|
|
|
|
|
|
foreach(l, tlist)
|
|
|
|
{
|
|
|
|
TargetEntry *tle = (TargetEntry *) lfirst(l);
|
|
|
|
|
|
|
|
if (tle->resjunk && !includeJunk)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
result = lappend(result, tle->expr);
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
Implement UPDATE tab SET (col1,col2,...) = (SELECT ...), ...
This SQL-standard feature allows a sub-SELECT yielding multiple columns
(but only one row) to be used to compute the new values of several columns
to be updated. While the same results can be had with an independent
sub-SELECT per column, such a workaround can require a great deal of
duplicated computation.
The standard actually says that the source for a multi-column assignment
could be any row-valued expression. The implementation used here is
tightly tied to our existing sub-SELECT support and can't handle other
cases; the Bison grammar would have some issues with them too. However,
I don't feel too bad about this since other cases can be converted into
sub-SELECTs. For instance, "SET (a,b,c) = row_valued_function(x)" could
be written "SET (a,b,c) = (SELECT * FROM row_valued_function(x))".
2014-06-18 19:22:25 +02:00
|
|
|
/*
|
|
|
|
* count_nonjunk_tlist_entries
|
|
|
|
* What it says ...
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
count_nonjunk_tlist_entries(List *tlist)
|
|
|
|
{
|
|
|
|
int len = 0;
|
|
|
|
ListCell *l;
|
|
|
|
|
|
|
|
foreach(l, tlist)
|
|
|
|
{
|
|
|
|
TargetEntry *tle = (TargetEntry *) lfirst(l);
|
|
|
|
|
|
|
|
if (!tle->resjunk)
|
|
|
|
len++;
|
|
|
|
}
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-03-14 18:42:51 +01:00
|
|
|
/*
|
|
|
|
* tlist_same_exprs
|
|
|
|
* Check whether two target lists contain the same expressions
|
|
|
|
*
|
|
|
|
* Note: this function is used to decide whether it's safe to jam a new tlist
|
|
|
|
* into a non-projection-capable plan node. Obviously we can't do that unless
|
|
|
|
* the node's tlist shows it already returns the column values we want.
|
|
|
|
* However, we can ignore the TargetEntry attributes resname, ressortgroupref,
|
|
|
|
* resorigtbl, resorigcol, and resjunk, because those are only labelings that
|
|
|
|
* don't affect the row values computed by the node. (Moreover, if we didn't
|
|
|
|
* ignore them, we'd frequently fail to make the desired optimization, since
|
|
|
|
* the planner tends to not bother to make resname etc. valid in intermediate
|
|
|
|
* plan nodes.) Note that on success, the caller must still jam the desired
|
|
|
|
* tlist into the plan node, else it won't have the desired labeling fields.
|
|
|
|
*/
|
|
|
|
bool
|
|
|
|
tlist_same_exprs(List *tlist1, List *tlist2)
|
|
|
|
{
|
|
|
|
ListCell *lc1,
|
|
|
|
*lc2;
|
|
|
|
|
|
|
|
if (list_length(tlist1) != list_length(tlist2))
|
|
|
|
return false; /* not same length, so can't match */
|
|
|
|
|
|
|
|
forboth(lc1, tlist1, lc2, tlist2)
|
|
|
|
{
|
|
|
|
TargetEntry *tle1 = (TargetEntry *) lfirst(lc1);
|
|
|
|
TargetEntry *tle2 = (TargetEntry *) lfirst(lc2);
|
|
|
|
|
|
|
|
if (!equal(tle1->expr, tle2->expr))
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-08-07 21:35:02 +02:00
|
|
|
/*
|
|
|
|
* Does tlist have same output datatypes as listed in colTypes?
|
|
|
|
*
|
|
|
|
* Resjunk columns are ignored if junkOK is true; otherwise presence of
|
|
|
|
* a resjunk column will always cause a 'false' result.
|
|
|
|
*
|
|
|
|
* Note: currently no callers care about comparing typmods.
|
|
|
|
*/
|
|
|
|
bool
|
|
|
|
tlist_same_datatypes(List *tlist, List *colTypes, bool junkOK)
|
|
|
|
{
|
|
|
|
ListCell *l;
|
|
|
|
ListCell *curColType = list_head(colTypes);
|
|
|
|
|
|
|
|
foreach(l, tlist)
|
|
|
|
{
|
|
|
|
TargetEntry *tle = (TargetEntry *) lfirst(l);
|
|
|
|
|
|
|
|
if (tle->resjunk)
|
|
|
|
{
|
|
|
|
if (!junkOK)
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (curColType == NULL)
|
|
|
|
return false; /* tlist longer than colTypes */
|
|
|
|
if (exprType((Node *) tle->expr) != lfirst_oid(curColType))
|
|
|
|
return false;
|
|
|
|
curColType = lnext(curColType);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (curColType != NULL)
|
|
|
|
return false; /* tlist shorter than colTypes */
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2011-04-16 22:39:50 +02:00
|
|
|
/*
|
|
|
|
* Does tlist have same exposed collations as listed in colCollations?
|
|
|
|
*
|
|
|
|
* Identical logic to the above, but for collations.
|
|
|
|
*/
|
|
|
|
bool
|
|
|
|
tlist_same_collations(List *tlist, List *colCollations, bool junkOK)
|
|
|
|
{
|
|
|
|
ListCell *l;
|
|
|
|
ListCell *curColColl = list_head(colCollations);
|
|
|
|
|
|
|
|
foreach(l, tlist)
|
|
|
|
{
|
|
|
|
TargetEntry *tle = (TargetEntry *) lfirst(l);
|
|
|
|
|
|
|
|
if (tle->resjunk)
|
|
|
|
{
|
|
|
|
if (!junkOK)
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (curColColl == NULL)
|
|
|
|
return false; /* tlist longer than colCollations */
|
|
|
|
if (exprCollation((Node *) tle->expr) != lfirst_oid(curColColl))
|
|
|
|
return false;
|
|
|
|
curColColl = lnext(curColColl);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (curColColl != NULL)
|
|
|
|
return false; /* tlist shorter than colCollations */
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2008-08-07 21:35:02 +02:00
|
|
|
|
1999-08-21 05:49:17 +02:00
|
|
|
/*
|
2007-11-08 22:49:48 +01:00
|
|
|
* get_sortgroupref_tle
|
|
|
|
* Find the targetlist entry matching the given SortGroupRef index,
|
|
|
|
* and return it.
|
1999-08-21 05:49:17 +02:00
|
|
|
*/
|
2000-01-27 19:11:50 +01:00
|
|
|
TargetEntry *
|
2007-11-08 22:49:48 +01:00
|
|
|
get_sortgroupref_tle(Index sortref, List *targetList)
|
1999-05-12 17:02:39 +02:00
|
|
|
{
|
2004-05-26 06:41:50 +02:00
|
|
|
ListCell *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
|
|
|
|
2007-11-08 22:49:48 +01:00
|
|
|
if (tle->ressortgroupref == sortref)
|
2000-01-27 19:11:50 +01:00
|
|
|
return tle;
|
1999-05-12 17:02:39 +02:00
|
|
|
}
|
|
|
|
|
2003-07-25 02:01:09 +02:00
|
|
|
elog(ERROR, "ORDER/GROUP BY expression not found in targetlist");
|
1999-08-21 05:49:17 +02:00
|
|
|
return NULL; /* keep compiler quiet */
|
1999-05-12 17:02:39 +02:00
|
|
|
}
|
2000-01-27 19:11:50 +01:00
|
|
|
|
2007-11-08 22:49:48 +01:00
|
|
|
/*
|
|
|
|
* get_sortgroupclause_tle
|
2008-08-02 23:32:01 +02:00
|
|
|
* Find the targetlist entry matching the given SortGroupClause
|
|
|
|
* by ressortgroupref, and return it.
|
2007-11-08 22:49:48 +01:00
|
|
|
*/
|
|
|
|
TargetEntry *
|
2008-08-02 23:32:01 +02:00
|
|
|
get_sortgroupclause_tle(SortGroupClause *sgClause,
|
2007-11-08 22:49:48 +01:00
|
|
|
List *targetList)
|
|
|
|
{
|
2008-08-02 23:32:01 +02:00
|
|
|
return get_sortgroupref_tle(sgClause->tleSortGroupRef, targetList);
|
2007-11-08 22:49:48 +01:00
|
|
|
}
|
|
|
|
|
2000-01-27 19:11:50 +01:00
|
|
|
/*
|
|
|
|
* get_sortgroupclause_expr
|
2008-08-02 23:32:01 +02:00
|
|
|
* Find the targetlist entry matching the given SortGroupClause
|
|
|
|
* by ressortgroupref, and return its expression.
|
2000-01-27 19:11:50 +01:00
|
|
|
*/
|
|
|
|
Node *
|
2008-08-02 23:32:01 +02:00
|
|
|
get_sortgroupclause_expr(SortGroupClause *sgClause, List *targetList)
|
2000-01-27 19:11:50 +01:00
|
|
|
{
|
2008-08-02 23:32:01 +02:00
|
|
|
TargetEntry *tle = get_sortgroupclause_tle(sgClause, targetList);
|
2000-01-27 19:11:50 +01:00
|
|
|
|
2002-12-12 16:49:42 +01:00
|
|
|
return (Node *) tle->expr;
|
2000-01-27 19:11:50 +01:00
|
|
|
}
|
2003-01-20 19:55:07 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* get_sortgrouplist_exprs
|
2008-08-02 23:32:01 +02:00
|
|
|
* Given a list of SortGroupClauses, build a list
|
2003-01-20 19:55:07 +01:00
|
|
|
* of the referenced targetlist expressions.
|
|
|
|
*/
|
|
|
|
List *
|
2008-08-02 23:32:01 +02:00
|
|
|
get_sortgrouplist_exprs(List *sgClauses, List *targetList)
|
2003-01-20 19:55:07 +01:00
|
|
|
{
|
2003-08-04 02:43:34 +02:00
|
|
|
List *result = NIL;
|
2004-05-26 06:41:50 +02:00
|
|
|
ListCell *l;
|
2003-01-20 19:55:07 +01:00
|
|
|
|
2008-08-02 23:32:01 +02:00
|
|
|
foreach(l, sgClauses)
|
2003-01-20 19:55:07 +01:00
|
|
|
{
|
2008-08-02 23:32:01 +02:00
|
|
|
SortGroupClause *sortcl = (SortGroupClause *) lfirst(l);
|
2003-01-20 19:55:07 +01:00
|
|
|
Node *sortexpr;
|
|
|
|
|
|
|
|
sortexpr = get_sortgroupclause_expr(sortcl, targetList);
|
|
|
|
result = lappend(result, sortexpr);
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
2006-02-03 22:08:49 +01:00
|
|
|
|
|
|
|
|
2008-08-07 03:11:52 +02:00
|
|
|
/*****************************************************************************
|
|
|
|
* Functions to extract data from a list of SortGroupClauses
|
|
|
|
*
|
|
|
|
* These don't really belong in tlist.c, but they are sort of related to the
|
|
|
|
* functions just above, and they don't seem to deserve their own file.
|
|
|
|
*****************************************************************************/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* extract_grouping_ops - make an array of the equality operator OIDs
|
|
|
|
* for a SortGroupClause list
|
|
|
|
*/
|
|
|
|
Oid *
|
|
|
|
extract_grouping_ops(List *groupClause)
|
|
|
|
{
|
|
|
|
int numCols = list_length(groupClause);
|
|
|
|
int colno = 0;
|
|
|
|
Oid *groupOperators;
|
|
|
|
ListCell *glitem;
|
|
|
|
|
|
|
|
groupOperators = (Oid *) palloc(sizeof(Oid) * numCols);
|
|
|
|
|
|
|
|
foreach(glitem, groupClause)
|
|
|
|
{
|
|
|
|
SortGroupClause *groupcl = (SortGroupClause *) lfirst(glitem);
|
|
|
|
|
|
|
|
groupOperators[colno] = groupcl->eqop;
|
|
|
|
Assert(OidIsValid(groupOperators[colno]));
|
|
|
|
colno++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return groupOperators;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* extract_grouping_cols - make an array of the grouping column resnos
|
|
|
|
* for a SortGroupClause list
|
|
|
|
*/
|
|
|
|
AttrNumber *
|
|
|
|
extract_grouping_cols(List *groupClause, List *tlist)
|
|
|
|
{
|
|
|
|
AttrNumber *grpColIdx;
|
|
|
|
int numCols = list_length(groupClause);
|
|
|
|
int colno = 0;
|
|
|
|
ListCell *glitem;
|
|
|
|
|
|
|
|
grpColIdx = (AttrNumber *) palloc(sizeof(AttrNumber) * numCols);
|
|
|
|
|
|
|
|
foreach(glitem, groupClause)
|
|
|
|
{
|
|
|
|
SortGroupClause *groupcl = (SortGroupClause *) lfirst(glitem);
|
|
|
|
TargetEntry *tle = get_sortgroupclause_tle(groupcl, tlist);
|
|
|
|
|
|
|
|
grpColIdx[colno++] = tle->resno;
|
|
|
|
}
|
|
|
|
|
|
|
|
return grpColIdx;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* grouping_is_sortable - is it possible to implement grouping list by sorting?
|
|
|
|
*
|
|
|
|
* This is easy since the parser will have included a sortop if one exists.
|
|
|
|
*/
|
|
|
|
bool
|
|
|
|
grouping_is_sortable(List *groupClause)
|
|
|
|
{
|
|
|
|
ListCell *glitem;
|
|
|
|
|
|
|
|
foreach(glitem, groupClause)
|
|
|
|
{
|
|
|
|
SortGroupClause *groupcl = (SortGroupClause *) lfirst(glitem);
|
|
|
|
|
|
|
|
if (!OidIsValid(groupcl->sortop))
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* grouping_is_hashable - is it possible to implement grouping list by hashing?
|
|
|
|
*
|
2010-10-31 02:55:20 +01:00
|
|
|
* We rely on the parser to have set the hashable flag correctly.
|
2008-08-07 03:11:52 +02:00
|
|
|
*/
|
|
|
|
bool
|
|
|
|
grouping_is_hashable(List *groupClause)
|
|
|
|
{
|
|
|
|
ListCell *glitem;
|
|
|
|
|
|
|
|
foreach(glitem, groupClause)
|
|
|
|
{
|
|
|
|
SortGroupClause *groupcl = (SortGroupClause *) lfirst(glitem);
|
|
|
|
|
2010-10-31 02:55:20 +01:00
|
|
|
if (!groupcl->hashable)
|
2008-08-07 03:11:52 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|