|
|
|
@ -8,7 +8,7 @@
|
|
|
|
|
*
|
|
|
|
|
*
|
|
|
|
|
* IDENTIFICATION
|
|
|
|
|
* $PostgreSQL: pgsql/src/backend/parser/parse_clause.c,v 1.190 2009/07/16 06:33:43 petere Exp $
|
|
|
|
|
* $PostgreSQL: pgsql/src/backend/parser/parse_clause.c,v 1.191 2009/08/27 20:08:02 tgl Exp $
|
|
|
|
|
*
|
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
@ -37,16 +37,15 @@
|
|
|
|
|
#include "utils/rel.h"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* clause types for findTargetlistEntrySQL92 */
|
|
|
|
|
#define ORDER_CLAUSE 0
|
|
|
|
|
#define GROUP_CLAUSE 1
|
|
|
|
|
#define DISTINCT_ON_CLAUSE 2
|
|
|
|
|
#define PARTITION_CLAUSE 3
|
|
|
|
|
|
|
|
|
|
static const char *const clauseText[] = {
|
|
|
|
|
"ORDER BY",
|
|
|
|
|
"GROUP BY",
|
|
|
|
|
"DISTINCT ON",
|
|
|
|
|
"PARTITION BY"
|
|
|
|
|
"DISTINCT ON"
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static void extractRemainingColumns(List *common_colnames,
|
|
|
|
@ -73,8 +72,10 @@ static Node *transformFromClauseItem(ParseState *pstate, Node *n,
|
|
|
|
|
Relids *containedRels);
|
|
|
|
|
static Node *buildMergedJoinVar(ParseState *pstate, JoinType jointype,
|
|
|
|
|
Var *l_colvar, Var *r_colvar);
|
|
|
|
|
static TargetEntry *findTargetlistEntry(ParseState *pstate, Node *node,
|
|
|
|
|
List **tlist, int clause);
|
|
|
|
|
static TargetEntry *findTargetlistEntrySQL92(ParseState *pstate, Node *node,
|
|
|
|
|
List **tlist, int clause);
|
|
|
|
|
static TargetEntry *findTargetlistEntrySQL99(ParseState *pstate, Node *node,
|
|
|
|
|
List **tlist);
|
|
|
|
|
static int get_matching_location(int sortgroupref,
|
|
|
|
|
List *sortgrouprefs, List *exprs);
|
|
|
|
|
static List *addTargetToSortList(ParseState *pstate, TargetEntry *tle,
|
|
|
|
@ -1216,21 +1217,27 @@ transformLimitClause(ParseState *pstate, Node *clause,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* findTargetlistEntry -
|
|
|
|
|
* findTargetlistEntrySQL92 -
|
|
|
|
|
* Returns the targetlist entry matching the given (untransformed) node.
|
|
|
|
|
* If no matching entry exists, one is created and appended to the target
|
|
|
|
|
* list as a "resjunk" node.
|
|
|
|
|
*
|
|
|
|
|
* This function supports the old SQL92 ORDER BY interpretation, where the
|
|
|
|
|
* expression is an output column name or number. If we fail to find a
|
|
|
|
|
* match of that sort, we fall through to the SQL99 rules. For historical
|
|
|
|
|
* reasons, Postgres also allows this interpretation for GROUP BY, though
|
|
|
|
|
* the standard never did. However, for GROUP BY we prefer a SQL99 match.
|
|
|
|
|
* This function is *not* used for WINDOW definitions.
|
|
|
|
|
*
|
|
|
|
|
* node the ORDER BY, GROUP BY, or DISTINCT ON expression to be matched
|
|
|
|
|
* tlist the target list (passed by reference so we can append to it)
|
|
|
|
|
* clause identifies clause type being processed
|
|
|
|
|
*/
|
|
|
|
|
static TargetEntry *
|
|
|
|
|
findTargetlistEntry(ParseState *pstate, Node *node, List **tlist, int clause)
|
|
|
|
|
findTargetlistEntrySQL92(ParseState *pstate, Node *node, List **tlist,
|
|
|
|
|
int clause)
|
|
|
|
|
{
|
|
|
|
|
TargetEntry *target_result = NULL;
|
|
|
|
|
ListCell *tl;
|
|
|
|
|
Node *expr;
|
|
|
|
|
|
|
|
|
|
/*----------
|
|
|
|
|
* Handle two special cases as mandated by the SQL92 spec:
|
|
|
|
@ -1258,8 +1265,7 @@ findTargetlistEntry(ParseState *pstate, Node *node, List **tlist, int clause)
|
|
|
|
|
* 2. IntegerConstant
|
|
|
|
|
* This means to use the n'th item in the existing target list.
|
|
|
|
|
* Note that it would make no sense to order/group/distinct by an
|
|
|
|
|
* actual constant, so this does not create a conflict with our
|
|
|
|
|
* extension to order/group by an expression.
|
|
|
|
|
* actual constant, so this does not create a conflict with SQL99.
|
|
|
|
|
* GROUP BY column-number is not allowed by SQL92, but since
|
|
|
|
|
* the standard has no other behavior defined for this syntax,
|
|
|
|
|
* we may as well accept this common extension.
|
|
|
|
@ -1268,7 +1274,7 @@ findTargetlistEntry(ParseState *pstate, Node *node, List **tlist, int clause)
|
|
|
|
|
* since the user didn't write them in his SELECT list.
|
|
|
|
|
*
|
|
|
|
|
* If neither special case applies, fall through to treat the item as
|
|
|
|
|
* an expression.
|
|
|
|
|
* an expression per SQL99.
|
|
|
|
|
*----------
|
|
|
|
|
*/
|
|
|
|
|
if (IsA(node, ColumnRef) &&
|
|
|
|
@ -1278,15 +1284,15 @@ findTargetlistEntry(ParseState *pstate, Node *node, List **tlist, int clause)
|
|
|
|
|
char *name = strVal(linitial(((ColumnRef *) node)->fields));
|
|
|
|
|
int location = ((ColumnRef *) node)->location;
|
|
|
|
|
|
|
|
|
|
if (clause == GROUP_CLAUSE || clause == PARTITION_CLAUSE)
|
|
|
|
|
if (clause == GROUP_CLAUSE)
|
|
|
|
|
{
|
|
|
|
|
/*
|
|
|
|
|
* In GROUP BY, we must prefer a match against a FROM-clause
|
|
|
|
|
* column to one against the targetlist. Look to see if there is
|
|
|
|
|
* a matching column. If so, fall through to let transformExpr()
|
|
|
|
|
* do the rest. NOTE: if name could refer ambiguously to more
|
|
|
|
|
* than one column name exposed by FROM, colNameToVar will
|
|
|
|
|
* ereport(ERROR). That's just what we want here.
|
|
|
|
|
* a matching column. If so, fall through to use SQL99 rules.
|
|
|
|
|
* NOTE: if name could refer ambiguously to more than one column
|
|
|
|
|
* name exposed by FROM, colNameToVar will ereport(ERROR). That's
|
|
|
|
|
* just what we want here.
|
|
|
|
|
*
|
|
|
|
|
* Small tweak for 7.4.3: ignore matches in upper query levels.
|
|
|
|
|
* This effectively changes the search order for bare names to (1)
|
|
|
|
@ -1295,8 +1301,6 @@ findTargetlistEntry(ParseState *pstate, Node *node, List **tlist, int clause)
|
|
|
|
|
* SQL99 do not allow GROUPing BY an outer reference, so this
|
|
|
|
|
* breaks no cases that are legal per spec, and it seems a more
|
|
|
|
|
* self-consistent behavior.
|
|
|
|
|
*
|
|
|
|
|
* Window PARTITION BY clauses should act exactly like GROUP BY.
|
|
|
|
|
*/
|
|
|
|
|
if (colNameToVar(pstate, name, true, location) != NULL)
|
|
|
|
|
name = NULL;
|
|
|
|
@ -1304,6 +1308,8 @@ findTargetlistEntry(ParseState *pstate, Node *node, List **tlist, int clause)
|
|
|
|
|
|
|
|
|
|
if (name != NULL)
|
|
|
|
|
{
|
|
|
|
|
TargetEntry *target_result = NULL;
|
|
|
|
|
|
|
|
|
|
foreach(tl, *tlist)
|
|
|
|
|
{
|
|
|
|
|
TargetEntry *tle = (TargetEntry *) lfirst(tl);
|
|
|
|
@ -1367,12 +1373,36 @@ findTargetlistEntry(ParseState *pstate, Node *node, List **tlist, int clause)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Otherwise, we have an expression (this is a Postgres extension not
|
|
|
|
|
* found in SQL92). Convert the untransformed node to a transformed
|
|
|
|
|
* expression, and search for a match in the tlist. NOTE: it doesn't
|
|
|
|
|
* really matter whether there is more than one match. Also, we are
|
|
|
|
|
* willing to match a resjunk target here, though the above cases must
|
|
|
|
|
* ignore resjunk targets.
|
|
|
|
|
* Otherwise, we have an expression, so process it per SQL99 rules.
|
|
|
|
|
*/
|
|
|
|
|
return findTargetlistEntrySQL99(pstate, node, tlist);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* findTargetlistEntrySQL99 -
|
|
|
|
|
* Returns the targetlist entry matching the given (untransformed) node.
|
|
|
|
|
* If no matching entry exists, one is created and appended to the target
|
|
|
|
|
* list as a "resjunk" node.
|
|
|
|
|
*
|
|
|
|
|
* This function supports the SQL99 interpretation, wherein the expression
|
|
|
|
|
* is just an ordinary expression referencing input column names.
|
|
|
|
|
*
|
|
|
|
|
* node the ORDER BY, GROUP BY, etc expression to be matched
|
|
|
|
|
* tlist the target list (passed by reference so we can append to it)
|
|
|
|
|
*/
|
|
|
|
|
static TargetEntry *
|
|
|
|
|
findTargetlistEntrySQL99(ParseState *pstate, Node *node, List **tlist)
|
|
|
|
|
{
|
|
|
|
|
TargetEntry *target_result;
|
|
|
|
|
ListCell *tl;
|
|
|
|
|
Node *expr;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Convert the untransformed node to a transformed expression, and search
|
|
|
|
|
* for a match in the tlist. NOTE: it doesn't really matter whether there
|
|
|
|
|
* is more than one match. Also, we are willing to match an existing
|
|
|
|
|
* resjunk target here, though the SQL92 cases above must ignore resjunk
|
|
|
|
|
* targets.
|
|
|
|
|
*/
|
|
|
|
|
expr = transformExpr(pstate, node);
|
|
|
|
|
|
|
|
|
@ -1403,16 +1433,15 @@ findTargetlistEntry(ParseState *pstate, Node *node, List **tlist, int clause)
|
|
|
|
|
* GROUP BY items will be added to the targetlist (as resjunk columns)
|
|
|
|
|
* if not already present, so the targetlist must be passed by reference.
|
|
|
|
|
*
|
|
|
|
|
* This is also used for window PARTITION BY clauses (which actually act
|
|
|
|
|
* just the same, except for the clause name used in error messages).
|
|
|
|
|
* This is also used for window PARTITION BY clauses (which act almost the
|
|
|
|
|
* same, but are always interpreted per SQL99 rules).
|
|
|
|
|
*/
|
|
|
|
|
List *
|
|
|
|
|
transformGroupClause(ParseState *pstate, List *grouplist,
|
|
|
|
|
List **targetlist, List *sortClause,
|
|
|
|
|
bool isPartition)
|
|
|
|
|
bool isWindowFunc)
|
|
|
|
|
{
|
|
|
|
|
List *result = NIL;
|
|
|
|
|
int clause = isPartition ? PARTITION_CLAUSE : GROUP_CLAUSE;
|
|
|
|
|
ListCell *gl;
|
|
|
|
|
|
|
|
|
|
foreach(gl, grouplist)
|
|
|
|
@ -1421,7 +1450,11 @@ transformGroupClause(ParseState *pstate, List *grouplist,
|
|
|
|
|
TargetEntry *tle;
|
|
|
|
|
bool found = false;
|
|
|
|
|
|
|
|
|
|
tle = findTargetlistEntry(pstate, gexpr, targetlist, clause);
|
|
|
|
|
if (isWindowFunc)
|
|
|
|
|
tle = findTargetlistEntrySQL99(pstate, gexpr, targetlist);
|
|
|
|
|
else
|
|
|
|
|
tle = findTargetlistEntrySQL92(pstate, gexpr, targetlist,
|
|
|
|
|
GROUP_CLAUSE);
|
|
|
|
|
|
|
|
|
|
/* Eliminate duplicates (GROUP BY x, x) */
|
|
|
|
|
if (targetIsInSortList(tle, InvalidOid, result))
|
|
|
|
@ -1475,12 +1508,16 @@ transformGroupClause(ParseState *pstate, List *grouplist,
|
|
|
|
|
*
|
|
|
|
|
* ORDER BY items will be added to the targetlist (as resjunk columns)
|
|
|
|
|
* if not already present, so the targetlist must be passed by reference.
|
|
|
|
|
*
|
|
|
|
|
* This is also used for window ORDER BY clauses (which act almost the
|
|
|
|
|
* same, but are always interpreted per SQL99 rules).
|
|
|
|
|
*/
|
|
|
|
|
List *
|
|
|
|
|
transformSortClause(ParseState *pstate,
|
|
|
|
|
List *orderlist,
|
|
|
|
|
List **targetlist,
|
|
|
|
|
bool resolveUnknown)
|
|
|
|
|
bool resolveUnknown,
|
|
|
|
|
bool isWindowFunc)
|
|
|
|
|
{
|
|
|
|
|
List *sortlist = NIL;
|
|
|
|
|
ListCell *olitem;
|
|
|
|
@ -1490,8 +1527,11 @@ transformSortClause(ParseState *pstate,
|
|
|
|
|
SortBy *sortby = (SortBy *) lfirst(olitem);
|
|
|
|
|
TargetEntry *tle;
|
|
|
|
|
|
|
|
|
|
tle = findTargetlistEntry(pstate, sortby->node,
|
|
|
|
|
targetlist, ORDER_CLAUSE);
|
|
|
|
|
if (isWindowFunc)
|
|
|
|
|
tle = findTargetlistEntrySQL99(pstate, sortby->node, targetlist);
|
|
|
|
|
else
|
|
|
|
|
tle = findTargetlistEntrySQL92(pstate, sortby->node, targetlist,
|
|
|
|
|
ORDER_CLAUSE);
|
|
|
|
|
|
|
|
|
|
sortlist = addTargetToSortList(pstate, tle,
|
|
|
|
|
sortlist, *targetlist, sortby,
|
|
|
|
@ -1550,18 +1590,19 @@ transformWindowDefinitions(ParseState *pstate,
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Transform PARTITION and ORDER specs, if any. These are treated
|
|
|
|
|
* exactly like top-level GROUP BY and ORDER BY clauses, including the
|
|
|
|
|
* special handling of nondefault operator semantics.
|
|
|
|
|
* almost exactly like top-level GROUP BY and ORDER BY clauses,
|
|
|
|
|
* including the special handling of nondefault operator semantics.
|
|
|
|
|
*/
|
|
|
|
|
orderClause = transformSortClause(pstate,
|
|
|
|
|
windef->orderClause,
|
|
|
|
|
targetlist,
|
|
|
|
|
true);
|
|
|
|
|
true /* fix unknowns */,
|
|
|
|
|
true /* window function */);
|
|
|
|
|
partitionClause = transformGroupClause(pstate,
|
|
|
|
|
windef->partitionClause,
|
|
|
|
|
targetlist,
|
|
|
|
|
orderClause,
|
|
|
|
|
true);
|
|
|
|
|
true /* window function */);
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* And prepare the new WindowClause.
|
|
|
|
@ -1736,8 +1777,8 @@ transformDistinctOnClause(ParseState *pstate, List *distinctlist,
|
|
|
|
|
int sortgroupref;
|
|
|
|
|
TargetEntry *tle;
|
|
|
|
|
|
|
|
|
|
tle = findTargetlistEntry(pstate, dexpr,
|
|
|
|
|
targetlist, DISTINCT_ON_CLAUSE);
|
|
|
|
|
tle = findTargetlistEntrySQL92(pstate, dexpr, targetlist,
|
|
|
|
|
DISTINCT_ON_CLAUSE);
|
|
|
|
|
sortgroupref = assignSortGroupRef(tle, *targetlist);
|
|
|
|
|
sortgrouprefs = lappend_int(sortgrouprefs, sortgroupref);
|
|
|
|
|
}
|
|
|
|
|