diff options
Diffstat (limited to 'src/backend/parser/parse_clause.c')
-rw-r--r-- | src/backend/parser/parse_clause.c | 121 |
1 files changed, 81 insertions, 40 deletions
diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c index 139202f6679..a63a280a8cb 100644 --- a/src/backend/parser/parse_clause.c +++ b/src/backend/parser/parse_clause.c @@ -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); } |