From 4ce24c8aa98b26f0b070464ce118ef2a1901064e Mon Sep 17 00:00:00 2001 From: Bruce Momjian Date: Sat, 27 Dec 1997 06:41:41 +0000 Subject: [PATCH] UNION work for UNION ALL and other union stuff. --- src/backend/executor/nodeAppend.c | 42 ++++----- src/backend/nodes/outfuncs.c | 4 +- src/backend/nodes/readfuncs.c | 3 +- src/backend/optimizer/plan/planner.c | 16 ++-- src/backend/optimizer/prep/prepunion.c | 118 ++++++++++++++++--------- src/backend/parser/analyze.c | 69 +++++++++++++-- src/include/nodes/parsenodes.h | 7 +- src/include/nodes/plannodes.h | 3 +- 8 files changed, 178 insertions(+), 84 deletions(-) diff --git a/src/backend/executor/nodeAppend.c b/src/backend/executor/nodeAppend.c index 3c07310127..cdc0e56a1b 100644 --- a/src/backend/executor/nodeAppend.c +++ b/src/backend/executor/nodeAppend.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/nodeAppend.c,v 1.9 1997/09/08 21:43:10 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/nodeAppend.c,v 1.10 1997/12/27 06:40:50 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -34,7 +34,7 @@ * nil nil ... ... ... * subplans * - * Append nodes are currently used to support inheritance + * Append nodes are currently used to unions, and to support inheritance * queries, where several relations need to be scanned. * For example, in our standard person/student/employee/student-emp * example, where student and employee inherit from person @@ -85,6 +85,7 @@ exec_append_initialize_next(Append *node) int whichplan; int nplans; + List *rts; List *rtentries; ResTarget *rtentry; @@ -101,6 +102,7 @@ exec_append_initialize_next(Append *node) whichplan = unionstate->as_whichplan; nplans = unionstate->as_nplans; + rts = node->unionrts; rtentries = node->unionrtentries; if (whichplan < 0) @@ -140,27 +142,28 @@ exec_append_initialize_next(Append *node) if (node->unionrelid > 0) { rtentry = nth(whichplan, rtentries); - if (rtentry == NULL) - elog(DEBUG, "exec_append_initialize_next: rtentry is nil"); + Assert(rtentry != NULL); unionrelid = node->unionrelid; rt_store(unionrelid, rangeTable, rtentry); - - if (unionstate->as_junkFilter_list) - { - estate->es_junkFilter = - (JunkFilter *) nth(whichplan, - unionstate->as_junkFilter_list); - } - if (unionstate->as_result_relation_info_list) - { - estate->es_result_relation_info = - (RelationInfo *) nth(whichplan, - unionstate->as_result_relation_info_list); - } - result_slot->ttc_whichplan = whichplan; } + else + estate->es_range_table = nth(whichplan, rts); + + if (unionstate->as_junkFilter_list) + { + estate->es_junkFilter = + (JunkFilter *) nth(whichplan, + unionstate->as_junkFilter_list); + } + if (unionstate->as_result_relation_info_list) + { + estate->es_result_relation_info = + (RelationInfo *) nth(whichplan, + unionstate->as_result_relation_info_list); + } + result_slot->ttc_whichplan = whichplan; return TRUE; } @@ -439,8 +442,7 @@ ExecProcAppend(Append *node) if (exec_append_initialize_next(node)) { ExecSetSlotDescriptorIsNew(result_slot, true); - return - ExecProcAppend(node); + return ExecProcAppend(node); } else return ExecClearTuple(result_slot); diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index 7b4bfc61e3..a7fa85d31f 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.13 1997/12/23 19:50:54 thomas Exp $ + * $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.14 1997/12/27 06:40:54 momjian Exp $ * * NOTES * Every (plan) node in POSTGRES has an associated "out" routine which @@ -194,7 +194,7 @@ _outQuery(StringInfo str, Query *node) sprintf(buf, " :qual "); appendStringInfo(str, buf); _outNode(str, node->qual); - + /* how are we handling aggregates, sort, and group by? bjm 1997/12/26 */ } /* diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c index 2fd639aaf4..8f1e6ff265 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.11 1997/12/18 12:53:59 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.12 1997/12/27 06:40:59 momjian Exp $ * * NOTES * Most of the read functions for plan nodes are tested. (In fact, they @@ -119,6 +119,7 @@ _readQuery() token = lsptok(NULL, &length); /* skip :qual */ local_node->qual = nodeRead(true); + /* how are we handling aggregates, sort, and group by? bjm 1997/12/26 */ return (local_node); } diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index cc3d9dfd7f..5c5e80f333 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.16 1997/12/24 06:06:01 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.17 1997/12/27 06:41:07 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -72,8 +72,6 @@ planner(Query *parse) { List *tlist = parse->targetList; List *rangetable = parse->rtable; - char *uniqueflag = parse->uniqueFlag; - List *sortclause = parse->sortClause; Plan *result_plan = (Plan *) NULL; @@ -83,7 +81,7 @@ planner(Query *parse) if (parse->unionClause) { - result_plan = (Plan *) plan_union_queries(0, /* none */ + result_plan = (Plan *) plan_union_queries( 0, /* none */ parse, UNION_FLAG); /* XXX do we need to do this? bjm 12/19/97 */ @@ -173,16 +171,16 @@ planner(Query *parse) * the optimization step later. */ - if (uniqueflag) + if (parse->uniqueFlag) { - Plan *sortplan = make_sortplan(tlist, sortclause, result_plan); + Plan *sortplan = make_sortplan(tlist, parse->sortClause, result_plan); - return ((Plan *) make_unique(tlist, sortplan, uniqueflag)); + return ((Plan *) make_unique(tlist, sortplan, parse->uniqueFlag)); } else { - if (sortclause) - return (make_sortplan(tlist, sortclause, result_plan)); + if (parse->sortClause) + return (make_sortplan(tlist, parse->sortClause, result_plan)); else return ((Plan *) result_plan); } diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c index 91d4f77709..c0386d2a12 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.14 1997/12/26 06:02:26 vadim Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepunion.c,v 1.15 1997/12/27 06:41:17 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -23,6 +23,7 @@ #include "nodes/relation.h" #include "parser/parsetree.h" +#include "parser/parse_clause.h" #include "utils/elog.h" #include "utils/lsyscache.h" @@ -42,7 +43,7 @@ static Query *subst_rangetable(Query *root, Index index, RangeTblEntry *new_entry); static void fix_parsetree_attnums(Index rt_index, Oid old_relid, Oid new_relid, Query *parsetree); -static Append *make_append(List *unionplans, Index rt_index, +static Append *make_append(List *unionplans, List *unionrts, Index rt_index, List *union_rt_entries, List *tlist); @@ -136,73 +137,102 @@ plan_union_queries(Index rt_index, Query *parse, UnionFlag flag) { - List *rangetable = parse->rtable; - RangeTblEntry *rt_entry = rt_fetch(rt_index, rangetable); - List *union_relids = NIL; List *union_plans = NIL; - List *union_rt_entries = NIL; switch (flag) { case INHERITS_FLAG: - 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, - rt_index, - union_rt_entries, - ((Plan *) lfirst(union_plans))->targetlist)); - break; + 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; + 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) - union_plans = lappend(union_plans, planner(lfirst(ulist))); - return (make_append(union_plans, - rt_index, rangetable, + { + 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 */ } @@ -392,6 +422,7 @@ fix_parsetree_attnums(Index rt_index, static Append * make_append(List *unionplans, + List *unionrts, Index rt_index, List *union_rt_entries, List *tlist) @@ -399,6 +430,7 @@ make_append(List *unionplans, Append *node = makeNode(Append); node->unionplans = unionplans; + node->unionrts = unionrts; node->unionrelid = rt_index; node->unionrtentries = union_rt_entries; node->plan.cost = 0.0; diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c index 05a1ecf335..edc9ae2be6 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.56 1997/12/24 06:06:18 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.57 1997/12/27 06:41:26 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -839,16 +839,75 @@ transformSelectStmt(ParseState *pstate, RetrieveStmt *stmt) if (pstate->p_numAgg > 0) finalizeAggregates(pstate, qry); + qry->unionall = stmt->unionall; /* in child, so unionClause may be false */ + if (stmt->unionClause) { List *ulist = NIL; QueryTreeList *qlist; - int i; - + int i, last_union = -1; + bool union_all_found = false, union_found = false; + 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++) - ulist = lappend(ulist, qlist->qtrees[i]); - qry->unionClause = ulist; + { + 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; diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index a6b9a400ea..d288d66aa3 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -6,7 +6,7 @@ * * Copyright (c) 1994, Regents of the University of California * - * $Id: parsenodes.h,v 1.39 1997/12/24 06:06:53 momjian Exp $ + * $Id: parsenodes.h,v 1.40 1997/12/27 06:41:39 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -43,7 +43,8 @@ typedef struct Query char *into; /* portal (cursor) name */ bool isPortal; /* is this a retrieve into portal? */ bool isBinary; /* binary portal? */ - + bool unionall; /* union without unique sort */ + char *uniqueFlag; /* NULL, '*', or Unique attribute name */ List *sortClause; /* a list of SortClause's */ @@ -636,7 +637,7 @@ typedef struct RetrieveStmt Node *havingClause; /* having conditional-expression */ List *unionClause; /* union subselect parameters */ List *sortClause; /* sort clause (a list of SortGroupBy's) */ - int unionall; /* union without unique sort */ + bool unionall; /* union without unique sort */ } RetrieveStmt; diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h index 63447c9369..2dc464c2a7 100644 --- a/src/include/nodes/plannodes.h +++ b/src/include/nodes/plannodes.h @@ -6,7 +6,7 @@ * * Copyright (c) 1994, Regents of the University of California * - * $Id: plannodes.h,v 1.11 1997/12/18 12:54:37 momjian Exp $ + * $Id: plannodes.h,v 1.12 1997/12/27 06:41:41 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -119,6 +119,7 @@ typedef struct Append { Plan plan; List *unionplans; + List *unionrts; Index unionrelid; List *unionrtentries; AppendState *unionstate; -- GitLab