diff options
Diffstat (limited to 'src/backend/parser')
-rw-r--r-- | src/backend/parser/analyze.c | 34 | ||||
-rw-r--r-- | src/backend/parser/gram.y | 31 | ||||
-rw-r--r-- | src/backend/parser/parse_clause.c | 175 |
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; } |