From a01b085c78e11838e2058ce032425079ded6edea Mon Sep 17 00:00:00 2001 From: Bruce Momjian Date: Mon, 29 Dec 1997 01:13:37 +0000 Subject: [PATCH] Cleanup of UNION ALL fix. Manual page updates. --- src/backend/optimizer/plan/planner.c | 14 +- src/backend/optimizer/prep/prepunion.c | 436 ++++++++++++++----------- src/backend/parser/analyze.c | 75 +---- src/backend/parser/parse_clause.c | 28 +- src/bin/psql/psqlHelp.h | 9 +- src/include/optimizer/prep.h | 16 +- src/include/parser/parse_clause.h | 3 +- src/man/select.l | 9 +- 8 files changed, 304 insertions(+), 286 deletions(-) diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index 5c5e80f333..bb33f49092 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.17 1997/12/27 06:41:07 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.18 1997/12/29 01:12:45 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -76,14 +76,12 @@ planner(Query *parse) Plan *result_plan = (Plan *) NULL; List *primary_qual; - int rt_index; + Index rt_index; if (parse->unionClause) { - result_plan = (Plan *) plan_union_queries( 0, /* none */ - parse, - UNION_FLAG); + result_plan = (Plan *) plan_union_queries(parse); /* XXX do we need to do this? bjm 12/19/97 */ tlist = preprocess_targetlist(tlist, parse->commandType, @@ -91,11 +89,9 @@ planner(Query *parse) parse->rtable); } else if ((rt_index = - first_matching_rt_entry(rangetable, INHERITS_FLAG)) != -1) + first_inherit_rt_entry(rangetable)) != -1) { - result_plan = (Plan *) plan_union_queries((Index) rt_index, - parse, - INHERITS_FLAG); + result_plan = (Plan *) plan_inherit_queries(parse, rt_index); /* XXX do we need to do this? bjm 12/19/97 */ tlist = preprocess_targetlist(tlist, parse->commandType, diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c index c0386d2a12..5cb89aac86 100644 --- a/src/backend/optimizer/prep/prepunion.c +++ b/src/backend/optimizer/prep/prepunion.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepunion.c,v 1.15 1997/12/27 06:41:17 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepunion.c,v 1.16 1997/12/29 01:12:48 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -34,8 +34,8 @@ #include "optimizer/planner.h" #include "optimizer/planmain.h" -static List *plan_union_query(List *relids, Index rt_index, - RangeTblEntry *rt_entry, Query *parse, UnionFlag flag, +static List *plan_inherit_query(List *relids, Index rt_index, + RangeTblEntry *rt_entry, Query *parse, List **union_rtentriesPtr); static RangeTblEntry *new_rangetable_entry(Oid new_relid, RangeTblEntry *old_entry); @@ -47,6 +47,248 @@ static Append *make_append(List *unionplans, List *unionrts, Index rt_index, List *union_rt_entries, List *tlist); +/* + * plan-union-queries-- + * + * Plans the queries for a given UNION. + * + * Returns a list containing a list of plans and a list of rangetables + */ +Append * +plan_union_queries(Query *parse) +{ + List *union_plans = NIL, *ulist, *unionall_queries, *union_rts, + *last_union = NIL; + bool union_all_found = false, union_found = false, + last_unionall_flag = false; + + /* + * Do we need to split up our unions because we have UNION + * and UNION ALL? + * + * We are checking for the case of: + * SELECT 1 + * UNION + * SELECT 2 + * UNION + * SELECT 3 + * UNION ALL + * SELECT 4 + * UNION ALL + * SELECT 5 + * + * where we have to do a DISTINCT on the output of the first three + * queries, then add the rest. If they have used UNION and + * UNION ALL, we grab all queries up to the last UNION query, + * make them their own UNION with the owner as the first query + * in the list. Then, we take the remaining queries, which is UNION + * ALL, and add them to the list of union queries. + * + * So the above query becomes: + * + * Append Node + * { + * Sort and Unique + * { + * Append Node + * { + * SELECT 1 This is really a sub-UNION, + * unionClause We run a DISTINCT on these. + * { + * SELECT 2 + * SELECT 3 + * } + * } + * } + * SELECT 4 + * SELECT 5 + * } + * + */ + + foreach(ulist, parse->unionClause) + { + Query *union_query = lfirst(ulist); + + if (union_query->unionall) + union_all_found = true; + else + { + union_found = true; + last_union = ulist; + } + last_unionall_flag = union_query->unionall; + } + + /* Is this a simple one */ + if (!union_all_found || + !union_found || + /* A trailing UNION negates the affect of earlier UNION ALLs */ + !last_unionall_flag) + { + List *hold_unionClause = parse->unionClause; + + parse->unionClause = NIL; /* prevent recursion */ + union_plans = lcons(planner(parse), NIL); + union_rts = lcons(parse->rtable, NIL); + + foreach(ulist, hold_unionClause) + { + Query *union_query = lfirst(ulist); + + union_plans = lappend(union_plans, planner(union_query)); + union_rts = lappend(union_rts, union_query->rtable); + } + } + else + { + /* + * We have mixed unions and non-unions + * + * We need to restructure this to put the UNIONs on their own + * so we can do a DISTINCT. + */ + + /* save off everthing past the last UNION */ + unionall_queries = lnext(last_union); + + /* clip off the list to remove the trailing UNION ALLs */ + lnext(last_union) = NIL; + + /* + * Recursion, but UNION only. + * The last one is a UNION, so it will not come here in recursion, + */ + union_plans = lcons(planner(parse), NIL); + union_rts = lcons(parse->rtable, NIL); + + /* Append the remainging UNION ALLs */ + foreach(ulist, unionall_queries) + { + Query *unionall_query = lfirst(ulist); + + union_plans = lappend(union_plans, planner(unionall_query)); + union_rts = lappend(union_rts, unionall_query->rtable); + } + } + + /* We have already split UNION and UNION ALL and we made it consistent */ + if (!last_unionall_flag) + { + parse->uniqueFlag = "*"; + parse->sortClause = transformSortClause(NULL, NIL, + ((Plan *) lfirst(union_plans))->targetlist, "*"); + } + else + { + /* needed so we don't take the flag from the first query */ + parse->uniqueFlag = NULL; + parse->sortClause = NIL; + } + + parse->havingQual = NULL; + parse->qry_numAgg = 0; + parse->qry_aggs = NULL; + + return (make_append(union_plans, + union_rts, + 0, + NULL, + ((Plan *) lfirst(union_plans))->targetlist)); +} + + +/* + * plan-inherit-queries-- + * + * Plans the queries for a given parent relation. + * + * Returns a list containing a list of plans and a list of rangetable + * entries to be inserted into an APPEND node. + * XXX - what exactly does this mean, look for make_append + */ +Append * +plan_inherit_queries(Query *parse, Index rt_index) +{ + List *union_plans = NIL; + + List *rangetable = parse->rtable; + RangeTblEntry *rt_entry = rt_fetch(rt_index, rangetable); + List *union_rt_entries = NIL; + List *union_relids = NIL; + + union_relids = + find_all_inheritors(lconsi(rt_entry->relid, + NIL), + NIL); + /* + * Remove the flag for this relation, since we're about to handle it + * (do it before recursing!). XXX destructive parse tree change + */ + rt_fetch(rt_index, rangetable)->inh = false; + + union_plans = plan_inherit_query(union_relids, rt_index, rt_entry, + parse, &union_rt_entries); + + return (make_append(union_plans, + NULL, + rt_index, + union_rt_entries, + ((Plan *) lfirst(union_plans))->targetlist)); +} + +/* + * plan-inherit-query-- + * Returns a list of plans for 'relids' and a list of range table entries + * in union_rtentries. + */ +static List * +plan_inherit_query(List *relids, + Index rt_index, + RangeTblEntry *rt_entry, + Query *root, + List **union_rtentriesPtr) +{ + List *i; + List *union_plans = NIL; + List *union_rtentries = NIL; + + foreach(i, relids) + { + int relid = lfirsti(i); + RangeTblEntry *new_rt_entry = new_rangetable_entry(relid, + rt_entry); + Query *new_root = subst_rangetable(root, + rt_index, + new_rt_entry); + + /* + * reset the uniqueflag and sortclause in parse tree root, so that + * sorting will only be done once after append + */ + new_root->uniqueFlag = NULL; + new_root->sortClause = NULL; + new_root->groupClause = NULL; + if (new_root->qry_numAgg != 0) + { + new_root->qry_numAgg = 0; + pfree(new_root->qry_aggs); + new_root->qry_aggs = NULL; + del_agg_tlist_references(new_root->targetList); + } + fix_parsetree_attnums(rt_index, + rt_entry->relid, + relid, + new_root); + + union_plans = lappend(union_plans, planner(new_root)); + union_rtentries = lappend(union_rtentries, new_rt_entry); + } + + *union_rtentriesPtr = union_rtentries; + return (union_plans); +} + /* * find-all-inheritors - * Returns a list of relids corresponding to relations that inherit @@ -91,14 +333,14 @@ find_all_inheritors(List *unexamined_relids, } /* - * first-matching-rt-entry - + * first-inherit-rt-entry - * Given a rangetable, find the first rangetable entry that represents * the appropriate special case. * * Returns a rangetable index., Returns -1 if no matches */ int -first_matching_rt_entry(List *rangetable, UnionFlag flag) +first_inherit_rt_entry(List *rangetable) { int count = 0; List *temp = NIL; @@ -107,188 +349,12 @@ first_matching_rt_entry(List *rangetable, UnionFlag flag) { RangeTblEntry *rt_entry = lfirst(temp); - switch (flag) - { - case INHERITS_FLAG: - if (rt_entry->inh) - return count + 1; - break; - default: - break; - } + if (rt_entry->inh) + return count + 1; count++; } - return (-1); -} - - -/* - * plan-union-queries-- - * - * Plans the queries for a given parent relation. - * - * Returns a list containing a list of plans and a list of rangetable - * entries to be inserted into an APPEND node. - * XXX - what exactly does this mean, look for make_append - */ -Append * -plan_union_queries(Index rt_index, - Query *parse, - UnionFlag flag) -{ - List *union_plans = NIL; - - switch (flag) - { - case INHERITS_FLAG: - { - List *rangetable = parse->rtable; - RangeTblEntry *rt_entry = rt_fetch(rt_index, rangetable); - List *union_rt_entries = NIL; - List *union_relids = NIL; - - union_relids = - find_all_inheritors(lconsi(rt_entry->relid, - NIL), - NIL); - /* - * Remove the flag for this relation, since we're about to handle it - * (do it before recursing!). XXX destructive parse tree change - */ - switch (flag) - { - case INHERITS_FLAG: - rt_fetch(rt_index, rangetable)->inh = false; - break; - default: - break; - } - - /* - * XXX - can't find any reason to sort union-relids as paul did, so - * we're leaving it out for now (maybe forever) - jeff & lp - * - * [maybe so. btw, jeff & lp did the lisp conversion, according to Paul. - * -- ay 10/94.] - */ - union_plans = plan_union_query(union_relids, rt_index, rt_entry, - parse, flag, &union_rt_entries); - - return (make_append(union_plans, - NULL, - rt_index, - union_rt_entries, - ((Plan *) lfirst(union_plans))->targetlist)); - break; - } - case UNION_FLAG: - { - List *ulist, *hold_union, *union_plans, *union_rts; - - hold_union = parse->unionClause; - parse->unionClause = NULL; /* prevent looping */ - - union_plans = lcons(planner(parse), NIL); - union_rts = lcons(parse->rtable, NIL); - foreach(ulist, hold_union) - { - Query *u = lfirst(ulist); - - union_plans = lappend(union_plans, planner(u)); - union_rts = lappend(union_rts, u->rtable); - } - - /* We have already split UNION and UNION ALL */ - if (!((Query *)lfirst(hold_union))->unionall) - { - parse->uniqueFlag = "*"; - parse->sortClause = transformSortClause(NULL, NIL, - ((Plan *)lfirst(union_plans))->targetlist, "*"); - } - else - { - /* needed so we don't take the flag from the first query */ - parse->uniqueFlag = NULL; - parse->sortClause = NIL; - } - - parse->havingQual = NULL; - parse->qry_numAgg = 0; - parse->qry_aggs = NULL; - - return (make_append(union_plans, union_rts, - rt_index /* is 0, none */, NULL, - ((Plan *) lfirst(union_plans))->targetlist)); - } - break; - -#ifdef NOT_USED - case VERSION_FLAG: - union_relids = VersionGetParents(rt_entry->relid); - break; -#endif - default: - /* do nothing */ - break; - } - return NULL; - - return ((Append*)NULL); /* to make gcc happy */ -} - - -/* - * plan-union-query-- - * Returns a list of plans for 'relids' and a list of range table entries - * in union_rtentries. - */ -static List * -plan_union_query(List *relids, - Index rt_index, - RangeTblEntry *rt_entry, - Query *root, - UnionFlag flag, - List **union_rtentriesPtr) -{ - List *i; - List *union_plans = NIL; - List *union_rtentries = NIL; - - foreach(i, relids) - { - int relid = lfirsti(i); - RangeTblEntry *new_rt_entry = new_rangetable_entry(relid, - rt_entry); - Query *new_root = subst_rangetable(root, - rt_index, - new_rt_entry); - - /* - * reset the uniqueflag and sortclause in parse tree root, so that - * sorting will only be done once after append - */ - new_root->uniqueFlag = NULL; - new_root->sortClause = NULL; - new_root->groupClause = NULL; - if (new_root->qry_numAgg != 0) - { - new_root->qry_numAgg = 0; - pfree(new_root->qry_aggs); - new_root->qry_aggs = NULL; - del_agg_tlist_references(new_root->targetList); - } - fix_parsetree_attnums(rt_index, - rt_entry->relid, - relid, - new_root); - - union_plans = lappend(union_plans, planner(new_root)); - union_rtentries = lappend(union_rtentries, new_rt_entry); - } - - *union_rtentriesPtr = union_rtentries; - return (union_plans); + return -1; } /* @@ -367,8 +433,6 @@ fix_parsetree_attnums_nodes(Index rt_index, Oid old_typeid, new_typeid; -/* old_typeid = RelationIdGetTypeId(old_relid);*/ -/* new_typeid = RelationIdGetTypeId(new_relid);*/ old_typeid = old_relid; new_typeid = new_relid; diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c index edc9ae2be6..0d1d3dcb24 100644 --- a/src/backend/parser/analyze.c +++ b/src/backend/parser/analyze.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.57 1997/12/27 06:41:26 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.58 1997/12/29 01:12:55 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -840,77 +840,10 @@ transformSelectStmt(ParseState *pstate, RetrieveStmt *stmt) finalizeAggregates(pstate, qry); qry->unionall = stmt->unionall; /* in child, so unionClause may be false */ - - if (stmt->unionClause) - { - List *ulist = NIL; - QueryTreeList *qlist; - int i, last_union = -1; - bool union_all_found = false, union_found = false; + qry->unionClause = transformUnionClause(stmt->unionClause, qry->targetList); - qlist = parse_analyze(stmt->unionClause); - - /* - * Do we need to split up our unions because we have UNION - * and UNION ALL? - */ - for (i=0; i < qlist->len; i++) - { - if (qlist->qtrees[i]->unionall) - union_all_found = true; - else - { - union_found = true; - last_union = i; - } - } - - /* A trailing UNION negates the affect of earlier UNION ALLs */ - if (!union_all_found || - !union_found || - /* last entry is a UNION */ - !qlist->qtrees[qlist->len-1]->unionall) - { - for (i=0; i < qlist->len; i++) - ulist = lappend(ulist, qlist->qtrees[i]); - qry->unionClause = ulist; - } - else - { - List *union_list = NIL; - Query *hold_qry; - - /* - * We have mixed unions and non-unions, so we concentrate on - * the last UNION in the list. - */ - for (i=0; i <= last_union; i++) - { - qlist->qtrees[i]->unionall = false; /*make queries consistent*/ - union_list = lappend(union_list, qlist->qtrees[i]); - } - - /* - * Make the first UNION ALL after the last UNION our new - * top query - */ - hold_qry = qry; - qry = qlist->qtrees[last_union + 1]; - qry->unionClause = lcons(hold_qry, NIL); /* UNION queries */ - hold_qry->unionall = true; /* UNION ALL this into other queries */ - hold_qry->unionClause = union_list; - - /* - * The first UNION ALL after the last UNION is our anchor, - * we skip it. - */ - for (i=last_union + 2; i < qlist->len; i++) - /* all queries are UNION ALL */ - qry->unionClause = lappend(qry->unionClause, qlist->qtrees[i]); - } - } - else - qry->unionClause = NULL; + if (qry->unionClause && stmt->sortClause) + elog(WARN, "You can not use ORDER BY with UNION", NULL); return (Query *) qry; } diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c index ad40222ba6..5368dc2e26 100644 --- a/src/backend/parser/parse_clause.c +++ b/src/backend/parser/parse_clause.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.3 1997/11/26 03:42:39 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.4 1997/12/29 01:12:58 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -18,6 +18,7 @@ #include "postgres.h" #include "access/heapam.h" #include "catalog/pg_type.h" +#include "parser/analyze.h" #include "parser/parse_clause.h" #include "parser/parse_expr.h" #include "parser/parse_node.h" @@ -371,3 +372,28 @@ transformSortClause(ParseState *pstate, return sortlist; } + +/* + * transformUnionClause - + * transform a Union clause + * + */ +List * +transformUnionClause(List *unionClause, List *targetlist) +{ + List *union_list = NIL; + QueryTreeList *qlist; + int i; +TargetEntry + if (unionClause) + { + qlist = parse_analyze(unionClause); + + for (i=0; i < qlist->len; i++) + union_list = lappend(union_list, qlist->qtrees[i]); + /* we need to check return types are consistent here */ + return union_list; + } + else + return NIL; +} diff --git a/src/bin/psql/psqlHelp.h b/src/bin/psql/psqlHelp.h index 27f820d26e..397e6979ad 100644 --- a/src/bin/psql/psqlHelp.h +++ b/src/bin/psql/psqlHelp.h @@ -5,7 +5,7 @@ * * Copyright (c) 1994, Regents of the University of California * - * $Id: psqlHelp.h,v 1.34 1997/12/04 00:27:37 scrappy Exp $ + * $Id: psqlHelp.h,v 1.35 1997/12/29 01:13:16 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -179,7 +179,12 @@ static struct _helpStruct QL_HELP[] = { "rollback [transaction|work]"}, {"select", "retrieve tuples", - "select [distinct on ] [as ], ... [as ]\n\t[into table ] [from ]\n\t[where ]\n\t[order by \n\t\t[using ],.. [[using ] | ASC | DESC]];"}, + "select [distinct on ] [as ], ... [as ]\n\ +\t[into table ]\n\ +\t[from ]\n\ +\t[where ]\n\ +\t[order by [ASC | DESC] [using ], ... ]\n\ +\t[union [all] select ...];"}, {"set", "set run-time environment", "set DateStyle to {'ISO' | 'SQL' | 'Postgres' | 'European' | 'US' | 'NonEuropean'}\nset GEQO to {'ON[=#]' | 'OFF'}\nset R_PLANS to {'ON' | 'OFF'}"}, diff --git a/src/include/optimizer/prep.h b/src/include/optimizer/prep.h index 7b22bb5f39..91e6d18993 100644 --- a/src/include/optimizer/prep.h +++ b/src/include/optimizer/prep.h @@ -6,7 +6,7 @@ * * Copyright (c) 1994, Regents of the University of California * - * $Id: prep.h,v 1.10 1997/12/24 06:06:58 momjian Exp $ + * $Id: prep.h,v 1.11 1997/12/29 01:13:23 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -27,18 +27,10 @@ extern List *cnfify(Expr *qual, bool removeAndFlag); extern List *preprocess_targetlist(List *tlist, int command_type, Index result_relation, List *range_table); -/* - * prototypes for prepunion.h - */ -typedef enum UnionFlag -{ - INHERITS_FLAG, UNION_FLAG, VERSION_FLAG -} UnionFlag; - extern List *find_all_inheritors(List *unexamined_relids, List *examined_relids); -extern int first_matching_rt_entry(List *rangetable, UnionFlag flag); -extern Append *plan_union_queries(Index rt_index, Query *parse, - UnionFlag flag); +extern int first_inherit_rt_entry(List *rangetable); +extern Append *plan_union_queries(Query *parse); +extern Append *plan_inherit_queries(Query *parse, Index rt_index); #endif /* PREP_H */ diff --git a/src/include/parser/parse_clause.h b/src/include/parser/parse_clause.h index 272bbc17b5..d402faeb11 100644 --- a/src/include/parser/parse_clause.h +++ b/src/include/parser/parse_clause.h @@ -6,7 +6,7 @@ * * Copyright (c) 1994, Regents of the University of California * - * $Id: parse_clause.h,v 1.3 1997/11/26 03:43:09 momjian Exp $ + * $Id: parse_clause.h,v 1.4 1997/12/29 01:13:28 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -26,6 +26,7 @@ extern List *transformGroupClause(ParseState *pstate, List *grouplist, extern List *transformSortClause(ParseState *pstate, List *orderlist, List *targetlist, char *uniqueFlag); +extern List *transformUnionClause(List *unionClause, List *targetlist); #endif /* PARSE_CLAUSE_H */ diff --git a/src/man/select.l b/src/man/select.l index c4ac5b8eec..cc36b2b5ac 100644 --- a/src/man/select.l +++ b/src/man/select.l @@ -1,6 +1,6 @@ .\" This is -*-nroff-*- .\" XXX standard disclaimer belongs here.... -.\" $Header: /cvsroot/pgsql/src/man/Attic/select.l,v 1.2 1996/12/11 00:28:09 momjian Exp $ +.\" $Header: /cvsroot/pgsql/src/man/Attic/select.l,v 1.3 1997/12/29 01:13:37 momjian Exp $ .TH SELECT SQL 11/05/95 PostgreSQL PostgreSQL .SH NAME select \(em retrieve instances from a class @@ -12,9 +12,10 @@ select \(em retrieve instances from a class [\fBinto\fR \fBtable\fR classname] [\fBfrom\fR from-list] [\fBwhere\fR where-clause] - [\fBgroup by\fR attr_name1 {, attr_name-i....} - [\fBorder by\fR attr_name1 - [\fBusing op1\fR] {, attr_namei [\fBusing opi\fR] } + [\fBgroup by\fR attr_name1 {, attr_name-i....}] + [\fBorder by\fR attr_name1 [\fBasc\fR | \fBdesc\fR] [\fBusing op1\fR] {, attr_namei...}] +[\fBunion {all} select\fR ...] + .fi .SH DESCRIPTION .BR Select