*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
#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,
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,
/*
- * 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:
* 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.
* 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) &&
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)
* 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;
if (name != NULL)
{
+ TargetEntry *target_result = NULL;
+
foreach(tl, *tlist)
{
TargetEntry *tle = (TargetEntry *) lfirst(tl);
}
/*
- * 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);
* 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)
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))
*
* 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;
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,
/*
* 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.
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);
}