aboutsummaryrefslogtreecommitdiff
path: root/src/backend/parser
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/parser')
-rw-r--r--src/backend/parser/analyze.c34
-rw-r--r--src/backend/parser/gram.y31
-rw-r--r--src/backend/parser/parse_clause.c175
3 files changed, 157 insertions, 83 deletions
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index 48178bcb215..fd3dda8f17e 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: analyze.c,v 1.133 2000/01/26 05:56:41 momjian Exp $
+ * $Id: analyze.c,v 1.134 2000/01/27 18:11:35 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -247,7 +247,7 @@ transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt)
makeRangeTable(pstate, NULL, NULL);
setTargetTable(pstate, stmt->relname);
- qry->uniqueFlag = NULL;
+ qry->distinctClause = NIL;
/* fix where clause */
qry->qual = transformWhereClause(pstate, stmt->whereClause, NULL);
@@ -296,8 +296,6 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
/* set up a range table --- note INSERT target is not in it yet */
makeRangeTable(pstate, stmt->fromClause, &fromQual);
- qry->uniqueFlag = stmt->unique;
-
qry->targetList = transformTargetList(pstate, stmt->targetList);
qry->qual = transformWhereClause(pstate, stmt->whereClause, fromQual);
@@ -311,13 +309,13 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
stmt->groupClause,
qry->targetList);
- /* An InsertStmt has no sortClause, but we still call
- * transformSortClause because it also handles uniqueFlag.
- */
- qry->sortClause = transformSortClause(pstate,
- NIL,
- qry->targetList,
- qry->uniqueFlag);
+ /* An InsertStmt has no sortClause */
+ qry->sortClause = NIL;
+
+ qry->distinctClause = transformDistinctClause(pstate,
+ stmt->distinctClause,
+ qry->targetList,
+ & qry->sortClause);
qry->hasSubLinks = pstate->p_hasSubLinks;
qry->hasAggs = pstate->p_hasAggs;
@@ -1312,8 +1310,6 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
/* set up a range table */
makeRangeTable(pstate, stmt->fromClause, &fromQual);
- qry->uniqueFlag = stmt->unique;
-
qry->into = stmt->into;
qry->isTemp = stmt->istemp;
qry->isPortal = FALSE;
@@ -1333,8 +1329,12 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
qry->sortClause = transformSortClause(pstate,
stmt->sortClause,
- qry->targetList,
- qry->uniqueFlag);
+ qry->targetList);
+
+ qry->distinctClause = transformDistinctClause(pstate,
+ stmt->distinctClause,
+ qry->targetList,
+ & qry->sortClause);
qry->hasSubLinks = pstate->p_hasSubLinks;
qry->hasAggs = pstate->p_hasAggs;
@@ -1558,9 +1558,9 @@ CheckSelectForUpdate(Query *qry)
{
if (qry->unionClause != NULL)
elog(ERROR, "SELECT FOR UPDATE is not allowed with UNION/INTERSECT/EXCEPT clause");
- if (qry->uniqueFlag != NULL)
+ if (qry->distinctClause != NIL)
elog(ERROR, "SELECT FOR UPDATE is not allowed with DISTINCT clause");
- if (qry->groupClause != NULL)
+ if (qry->groupClause != NIL)
elog(ERROR, "SELECT FOR UPDATE is not allowed with GROUP BY clause");
if (qry->hasAggs)
elog(ERROR, "SELECT FOR UPDATE is not allowed with AGGREGATE");
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 924d26d26fb..228cb73f3af 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -11,7 +11,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.135 2000/01/26 05:56:41 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.136 2000/01/27 18:11:35 tgl Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
@@ -159,7 +159,7 @@ static Node *doNegate(Node *n);
class, index_name, name, func_name, file_name, aggr_argtype
%type <str> opt_id,
- all_Op, MathOp, opt_name, opt_unique,
+ all_Op, MathOp, opt_name,
OptUseOp, opt_class, SpecialRuleRelation
%type <str> opt_level, opt_encoding
@@ -168,7 +168,7 @@ static Node *doNegate(Node *n);
%type <list> stmtblock, stmtmulti,
result, relation_name_list, OptTableElementList,
- OptInherit, definition,
+ OptInherit, definition, opt_distinct,
opt_with, func_args, func_args_list, func_as,
oper_argtypes, RuleActionList, RuleActionMulti,
opt_column_list, columnList, opt_va_list, va_list,
@@ -2843,7 +2843,7 @@ insert_rest: VALUES '(' target_list ')'
{
$$ = makeNode(InsertStmt);
$$->cols = NULL;
- $$->unique = NULL;
+ $$->distinctClause = NIL;
$$->targetList = $3;
$$->fromClause = NIL;
$$->whereClause = NULL;
@@ -2854,7 +2854,7 @@ insert_rest: VALUES '(' target_list ')'
| DEFAULT VALUES
{
$$ = makeNode(InsertStmt);
- $$->unique = NULL;
+ $$->distinctClause = NIL;
$$->targetList = NIL;
$$->fromClause = NIL;
$$->whereClause = NULL;
@@ -2873,7 +2873,7 @@ insert_rest: VALUES '(' target_list ')'
elog(ERROR, "INSERT ... SELECT can't have ORDER BY");
$$ = makeNode(InsertStmt);
$$->cols = NIL;
- $$->unique = n->unique;
+ $$->distinctClause = n->distinctClause;
$$->targetList = n->targetList;
$$->fromClause = n->fromClause;
$$->whereClause = n->whereClause;
@@ -2888,7 +2888,7 @@ insert_rest: VALUES '(' target_list ')'
{
$$ = makeNode(InsertStmt);
$$->cols = $2;
- $$->unique = NULL;
+ $$->distinctClause = NIL;
$$->targetList = $6;
$$->fromClause = NIL;
$$->whereClause = NULL;
@@ -2904,7 +2904,7 @@ insert_rest: VALUES '(' target_list ')'
elog(ERROR, "INSERT ... SELECT can't have ORDER BY");
$$ = makeNode(InsertStmt);
$$->cols = $2;
- $$->unique = n->unique;
+ $$->distinctClause = n->distinctClause;
$$->targetList = n->targetList;
$$->fromClause = n->fromClause;
$$->whereClause = n->whereClause;
@@ -3189,12 +3189,12 @@ select_clause: '(' select_clause ')'
}
;
-SubSelect: SELECT opt_unique target_list
+SubSelect: SELECT opt_distinct target_list
result from_clause where_clause
group_clause having_clause
{
SelectStmt *n = makeNode(SelectStmt);
- n->unique = $2;
+ n->distinctClause = $2;
n->unionall = FALSE;
n->targetList = $3;
/* This is new: Subselects support the INTO clause
@@ -3230,10 +3230,13 @@ opt_all: ALL { $$ = TRUE; }
| /*EMPTY*/ { $$ = FALSE; }
;
-opt_unique: DISTINCT { $$ = "*"; }
- | DISTINCT ON ColId { $$ = $3; }
- | ALL { $$ = NULL; }
- | /*EMPTY*/ { $$ = NULL; }
+/* We use (NIL) as a placeholder to indicate that all target expressions
+ * should be placed in the DISTINCT list during parsetree analysis.
+ */
+opt_distinct: DISTINCT { $$ = lcons(NIL,NIL); }
+ | DISTINCT ON '(' expr_list ')' { $$ = $4; }
+ | ALL { $$ = NIL; }
+ | /*EMPTY*/ { $$ = NIL; }
;
sort_clause: ORDER BY sortby_list { $$ = $3; }
diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c
index ba2b5f8499b..b22691fa3c2 100644
--- a/src/backend/parser/parse_clause.c
+++ b/src/backend/parser/parse_clause.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.50 2000/01/26 05:56:42 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.51 2000/01/27 18:11:35 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -28,12 +28,12 @@
#define ORDER_CLAUSE 0
#define GROUP_CLAUSE 1
+#define DISTINCT_ON_CLAUSE 2
-static char *clauseText[] = {"ORDER", "GROUP"};
+static char *clauseText[] = {"ORDER BY", "GROUP BY", "DISTINCT ON"};
static TargetEntry *findTargetlistEntry(ParseState *pstate, Node *node,
- List *tlist, int clause,
- char *uniqFlag);
+ List *tlist, int clause);
static void parseFromClause(ParseState *pstate, List *frmList, Node **qual);
static char *transformTableEntry(ParseState *pstate, RangeVar *r);
static List *addTargetToSortList(TargetEntry *tle, List *sortlist,
@@ -359,14 +359,13 @@ parseFromClause(ParseState *pstate, List *frmList, Node **qual)
* If no matching entry exists, one is created and appended to the target
* list as a "resjunk" node.
*
- * node the ORDER BY or GROUP BY expression to be matched
+ * node the ORDER BY, GROUP BY, or DISTINCT ON expression to be matched
* tlist the existing target list (NB: this cannot be NIL, which is a
* good thing since we'd be unable to append to it...)
* clause identifies clause type for error messages.
*/
static TargetEntry *
-findTargetlistEntry(ParseState *pstate, Node *node, List *tlist, int clause,
- char *uniqueFlag)
+findTargetlistEntry(ParseState *pstate, Node *node, List *tlist, int clause)
{
TargetEntry *target_result = NULL;
List *tl;
@@ -407,7 +406,7 @@ findTargetlistEntry(ParseState *pstate, Node *node, List *tlist, int clause,
if (target_result != NULL)
{
if (! equal(target_result->expr, tle->expr))
- elog(ERROR, "%s BY '%s' is ambiguous",
+ elog(ERROR, "%s '%s' is ambiguous",
clauseText[clause], name);
}
else
@@ -424,8 +423,8 @@ findTargetlistEntry(ParseState *pstate, Node *node, List *tlist, int clause,
int targetlist_pos = 0;
int target_pos;
- if (nodeTag(val) != T_Integer)
- elog(ERROR, "Non-integer constant in %s BY", clauseText[clause]);
+ if (! IsA(val, Integer))
+ elog(ERROR, "Non-integer constant in %s", clauseText[clause]);
target_pos = intVal(val);
foreach(tl, tlist)
{
@@ -438,7 +437,7 @@ findTargetlistEntry(ParseState *pstate, Node *node, List *tlist, int clause,
return tle; /* return the unique match */
}
}
- elog(ERROR, "%s BY position %d is not in target list",
+ elog(ERROR, "%s position %d is not in target list",
clauseText[clause], target_pos);
}
@@ -462,13 +461,9 @@ findTargetlistEntry(ParseState *pstate, Node *node, List *tlist, int clause,
/*
* If no matches, construct a new target entry which is appended to
- * the end of the target list. This target is set to be resjunk =
- * TRUE so that it will not be projected into the final tuple.
+ * the end of the target list. This target is given resjunk = TRUE
+ * so that it will not be projected into the final tuple.
*/
- if(clause == ORDER_CLAUSE && uniqueFlag) {
- elog(ERROR, "ORDER BY columns must appear in SELECT DISTINCT target list");
- }
-
target_result = transformTargetEntry(pstate, node, expr, NULL, true);
lappend(tlist, target_result);
@@ -492,7 +487,7 @@ transformGroupClause(ParseState *pstate, List *grouplist, List *targetlist)
TargetEntry *tle;
tle = findTargetlistEntry(pstate, lfirst(gl),
- targetlist, GROUP_CLAUSE, NULL);
+ targetlist, GROUP_CLAUSE);
/* avoid making duplicate grouplist entries */
if (! exprIsInSortList(tle->expr, glist, targetlist))
@@ -514,74 +509,149 @@ transformGroupClause(ParseState *pstate, List *grouplist, List *targetlist)
/*
* transformSortClause -
- * transform an Order By clause
- *
+ * transform an ORDER BY clause
*/
List *
transformSortClause(ParseState *pstate,
List *orderlist,
- List *targetlist,
- char *uniqueFlag)
+ List *targetlist)
{
List *sortlist = NIL;
List *olitem;
- /* Transform all the explicit ORDER BY clauses */
-
foreach(olitem, orderlist)
{
SortGroupBy *sortby = lfirst(olitem);
TargetEntry *tle;
tle = findTargetlistEntry(pstate, sortby->node,
- targetlist, ORDER_CLAUSE, uniqueFlag);
+ targetlist, ORDER_CLAUSE);
sortlist = addTargetToSortList(tle, sortlist, targetlist,
sortby->useOp);
}
- /* If we have a DISTINCT clause, add any necessary entries to
- * the sortlist to ensure that all the DISTINCT columns will be
- * sorted. A subsequent UNIQUE pass will then do the right thing.
- */
+ return sortlist;
+}
- if (uniqueFlag)
+/*
+ * transformDistinctClause -
+ * transform a DISTINCT or DISTINCT ON clause
+ *
+ * Since we may need to add items to the query's sortClause list, that list
+ * is passed by reference. We might also need to add items to the query's
+ * targetlist, but we assume that cannot be empty initially, so we can
+ * lappend to it even though the pointer is passed by value.
+ */
+List *
+transformDistinctClause(ParseState *pstate, List *distinctlist,
+ List *targetlist, List **sortClause)
+{
+ List *result = NIL;
+ List *slitem;
+ List *dlitem;
+
+ /* No work if there was no DISTINCT clause */
+ if (distinctlist == NIL)
+ return NIL;
+
+ if (lfirst(distinctlist) == NIL)
{
- if (uniqueFlag[0] == '*')
+ /* We had SELECT DISTINCT */
+
+ /*
+ * All non-resjunk elements from target list that are not already
+ * in the sort list should be added to it. (We don't really care
+ * what order the DISTINCT fields are checked in, so we can leave
+ * the user's ORDER BY spec alone, and just add additional sort keys
+ * to it to ensure that all targetlist items get sorted.)
+ */
+ *sortClause = addAllTargetsToSortList(*sortClause, targetlist);
+
+ /*
+ * Now, DISTINCT list consists of all non-resjunk sortlist items.
+ * Actually, all the sortlist items had better be non-resjunk!
+ * Otherwise, user wrote SELECT DISTINCT with an ORDER BY item
+ * that does not appear anywhere in the SELECT targetlist, and
+ * we can't implement that with only one sorting pass...
+ */
+ foreach(slitem, *sortClause)
{
- /*
- * concatenate all elements from target list that are not
- * already in the sortby list
- */
- sortlist = addAllTargetsToSortList(sortlist, targetlist);
+ SortClause *scl = (SortClause *) lfirst(slitem);
+ TargetEntry *tle = get_sortgroupclause_tle(scl, targetlist);
+
+ if (tle->resdom->resjunk)
+ elog(ERROR, "For SELECT DISTINCT, ORDER BY expressions must appear in target list");
+ else
+ result = lappend(result, copyObject(scl));
}
- else
+ }
+ else
+ {
+ /* We had SELECT DISTINCT ON (expr, ...) */
+
+ /*
+ * If the user writes both DISTINCT ON and ORDER BY, then the two
+ * expression lists must match (until one or the other runs out).
+ * Otherwise the ORDER BY requires a different sort order than the
+ * DISTINCT does, and we can't implement that with only one sort pass
+ * (and if we do two passes, the results will be rather unpredictable).
+ * However, it's OK to have more DISTINCT ON expressions than ORDER BY
+ * expressions; we can just add the extra DISTINCT values to the sort
+ * list, much as we did above for ordinary DISTINCT fields.
+ *
+ * Actually, it'd be OK for the common prefixes of the two lists to
+ * match in any order, but implementing that check seems like more
+ * trouble than it's worth.
+ */
+ List *nextsortlist = *sortClause;
+
+ foreach(dlitem, distinctlist)
{
- TargetEntry *tle = NULL;
- char *uniqueAttrName = uniqueFlag;
- List *i;
+ TargetEntry *tle;
- /* only create sort clause with the specified unique attribute */
- foreach(i, targetlist)
+ tle = findTargetlistEntry(pstate, lfirst(dlitem),
+ targetlist, DISTINCT_ON_CLAUSE);
+
+ if (nextsortlist != NIL)
{
- tle = (TargetEntry *) lfirst(i);
- if (strcmp(tle->resdom->resname, uniqueAttrName) == 0)
- break;
+ SortClause *scl = (SortClause *) lfirst(nextsortlist);
+
+ if (tle->resdom->ressortgroupref != scl->tleSortGroupRef)
+ elog(ERROR, "SELECT DISTINCT ON expressions must match initial ORDER BY expressions");
+ result = lappend(result, copyObject(scl));
+ nextsortlist = lnext(nextsortlist);
}
- if (i == NIL)
- elog(ERROR, "All fields in the UNIQUE ON clause must appear in the target list");
+ else
+ {
+ *sortClause = addTargetToSortList(tle, *sortClause,
+ targetlist, NULL);
+ /* Probably, the tle should always have been added at the
+ * end of the sort list ... but search to be safe.
+ */
+ foreach(slitem, *sortClause)
+ {
+ SortClause *scl = (SortClause *) lfirst(slitem);
- sortlist = addTargetToSortList(tle, sortlist, targetlist, NULL);
+ if (tle->resdom->ressortgroupref == scl->tleSortGroupRef)
+ {
+ result = lappend(result, copyObject(scl));
+ break;
+ }
+ }
+ if (slitem == NIL)
+ elog(ERROR, "transformDistinctClause: failed to add DISTINCT ON clause to target list");
+ }
}
}
- return sortlist;
+ return result;
}
/*
* addAllTargetsToSortList
- * Make sure all targets in the targetlist are in the ORDER BY list,
- * adding the not-yet-sorted ones to the end of the list.
+ * Make sure all non-resjunk targets in the targetlist are in the
+ * ORDER BY list, adding the not-yet-sorted ones to the end of the list.
* This is typically used to help implement SELECT DISTINCT.
*
* Returns the updated ORDER BY list.
@@ -595,7 +665,8 @@ addAllTargetsToSortList(List *sortlist, List *targetlist)
{
TargetEntry *tle = (TargetEntry *) lfirst(i);
- sortlist = addTargetToSortList(tle, sortlist, targetlist, NULL);
+ if (! tle->resdom->resjunk)
+ sortlist = addTargetToSortList(tle, sortlist, targetlist, NULL);
}
return sortlist;
}