diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c index fc95d2ce5b..b600c0e3a8 100644 --- a/src/backend/executor/nodeAgg.c +++ b/src/backend/executor/nodeAgg.c @@ -23,6 +23,7 @@ #include "executor/executor.h" #include "executor/nodeAgg.h" #include "optimizer/clauses.h" +#include "optimizer/planmain.h" #include "parser/parse_type.h" #include "utils/syscache.h" @@ -91,7 +92,7 @@ ExecAgg(Agg *node) EState *estate; Plan *outerPlan; int aggno, - nagg; + numaggs; Datum *value1, *value2; int *noInitValue; @@ -128,19 +129,19 @@ ExecAgg(Agg *node) estate = node->plan.state; econtext = aggstate->csstate.cstate.cs_ExprContext; - nagg = length(node->aggs); + numaggs = length(aggstate->aggs); value1 = node->aggstate->csstate.cstate.cs_ExprContext->ecxt_values; nulls = node->aggstate->csstate.cstate.cs_ExprContext->ecxt_nulls; - value2 = (Datum *) palloc(sizeof(Datum) * nagg); - MemSet(value2, 0, sizeof(Datum) * nagg); + value2 = (Datum *) palloc(sizeof(Datum) * numaggs); + MemSet(value2, 0, sizeof(Datum) * numaggs); - aggFuncInfo = (AggFuncInfo *) palloc(sizeof(AggFuncInfo) * nagg); - MemSet(aggFuncInfo, 0, sizeof(AggFuncInfo) * nagg); + aggFuncInfo = (AggFuncInfo *) palloc(sizeof(AggFuncInfo) * numaggs); + MemSet(aggFuncInfo, 0, sizeof(AggFuncInfo) * numaggs); - noInitValue = (int *) palloc(sizeof(int) * nagg); - MemSet(noInitValue, 0, sizeof(int) * nagg); + noInitValue = (int *) palloc(sizeof(int) * numaggs); + MemSet(noInitValue, 0, sizeof(int) * numaggs); outerPlan = outerPlan(node); oneTuple = NULL; @@ -148,7 +149,7 @@ ExecAgg(Agg *node) projInfo = aggstate->csstate.cstate.cs_ProjInfo; aggno = -1; - foreach(alist, node->aggs) + foreach(alist, aggstate->aggs) { Aggref *aggref = lfirst(alist); char *aggname; @@ -269,7 +270,7 @@ ExecAgg(Agg *node) } aggno = -1; - foreach(alist, node->aggs) + foreach(alist, aggstate->aggs) { Aggref *aggref = lfirst(alist); AggFuncInfo *aggfns = &aggFuncInfo[++aggno]; @@ -367,7 +368,7 @@ ExecAgg(Agg *node) */ aggno = -1; - foreach(alist, node->aggs) + foreach(alist, aggstate->aggs) { char *args[2]; AggFuncInfo *aggfns = &aggFuncInfo[++aggno]; @@ -467,6 +468,7 @@ ExecInitAgg(Agg *node, EState *estate, Plan *parent) AggState *aggstate; Plan *outerPlan; ExprContext *econtext; + int numaggs; /* * assign the node's execution state @@ -478,7 +480,16 @@ ExecInitAgg(Agg *node, EState *estate, Plan *parent) */ aggstate = makeNode(AggState); node->aggstate = aggstate; - aggstate->agg_done = FALSE; + aggstate->agg_done = false; + + /* + * find aggregates in targetlist and quals + */ + aggstate->aggs = nconc(pull_agg_clause((Node *) node->plan.targetlist), + pull_agg_clause((Node *) node->plan.qual)); + numaggs = length(aggstate->aggs); + if (numaggs <= 0) + elog(ERROR, "ExecInitAgg: could not find any aggregate functions"); /* * assign node's base id and create expression context @@ -495,10 +506,10 @@ ExecInitAgg(Agg *node, EState *estate, Plan *parent) ExecInitResultTupleSlot(estate, &aggstate->csstate.cstate); econtext = aggstate->csstate.cstate.cs_ExprContext; - econtext->ecxt_values = (Datum *) palloc(sizeof(Datum) * length(node->aggs)); - MemSet(econtext->ecxt_values, 0, sizeof(Datum) * length(node->aggs)); - econtext->ecxt_nulls = (char *) palloc(sizeof(char) * length(node->aggs)); - MemSet(econtext->ecxt_nulls, 0, sizeof(char) * length(node->aggs)); + econtext->ecxt_values = (Datum *) palloc(sizeof(Datum) * numaggs); + MemSet(econtext->ecxt_values, 0, sizeof(Datum) * numaggs); + econtext->ecxt_nulls = (char *) palloc(sizeof(char) * numaggs); + MemSet(econtext->ecxt_nulls, 0, sizeof(char) * numaggs); /* * initializes child nodes @@ -510,7 +521,7 @@ ExecInitAgg(Agg *node, EState *estate, Plan *parent) * Result runs in its own context, but make it use our aggregates fix * for 'select sum(2+2)' */ - if (nodeTag(outerPlan) == T_Result) + if (IsA(outerPlan, Result)) { ((Result *) outerPlan)->resstate->cstate.cs_ProjInfo->pi_exprContext->ecxt_values = econtext->ecxt_values; @@ -645,9 +656,9 @@ ExecReScanAgg(Agg *node, ExprContext *exprCtxt, Plan *parent) AggState *aggstate = node->aggstate; ExprContext *econtext = aggstate->csstate.cstate.cs_ExprContext; - aggstate->agg_done = FALSE; - MemSet(econtext->ecxt_values, 0, sizeof(Datum) * length(node->aggs)); - MemSet(econtext->ecxt_nulls, 0, sizeof(char) * length(node->aggs)); + aggstate->agg_done = false; + MemSet(econtext->ecxt_values, 0, sizeof(Datum) * length(aggstate->aggs)); + MemSet(econtext->ecxt_nulls, 0, sizeof(char) * length(aggstate->aggs)); /* * if chgParam of subnode is not null then plan will be re-scanned by diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index 99b1c39c7e..4895be7237 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.91 1999/08/16 02:17:41 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.92 1999/08/21 03:48:57 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -456,12 +456,6 @@ _copyAgg(Agg *from) CopyPlanFields((Plan *) from, (Plan *) newnode); - /* - * Cannot copy agg list; it must be rebuilt to point to subnodes of - * new node. - */ - set_agg_tlist_references(newnode); - return newnode; } @@ -474,8 +468,8 @@ _copyGroupClause(GroupClause *from) { GroupClause *newnode = makeNode(GroupClause); - newnode->grpOpoid = from->grpOpoid; - newnode->tleGroupref = from->tleGroupref; + newnode->tleSortGroupRef = from->tleSortGroupRef; + newnode->sortop = from->sortop; return newnode; } @@ -567,12 +561,11 @@ _copyResdom(Resdom *from) newnode->resno = from->resno; newnode->restype = from->restype; newnode->restypmod = from->restypmod; - if (from->resname != NULL) newnode->resname = pstrdup(from->resname); + newnode->ressortgroupref = from->ressortgroupref; newnode->reskey = from->reskey; newnode->reskeyop = from->reskeyop; - newnode->resgroupref = from->resgroupref; newnode->resjunk = from->resjunk; return newnode; @@ -862,8 +855,8 @@ _copyAggref(Aggref *from) newnode->basetype = from->basetype; newnode->aggtype = from->aggtype; Node_Copy(from, newnode, target); - newnode->aggno = from->aggno; newnode->usenulls = from->usenulls; + newnode->aggno = from->aggno; /* probably not needed */ return newnode; } @@ -1345,8 +1338,8 @@ _copySortClause(SortClause *from) { SortClause *newnode = makeNode(SortClause); - Node_Copy(from, newnode, resdom); - newnode->opoid = from->opoid; + newnode->tleSortGroupRef = from->tleSortGroupRef; + newnode->sortop = from->sortop; return newnode; } @@ -1398,33 +1391,29 @@ _copyQuery(Query *from) newnode->isBinary = from->isBinary; newnode->isTemp = from->isTemp; newnode->unionall = from->unionall; - if (from->uniqueFlag) - newnode->uniqueFlag = pstrdup(from->uniqueFlag); - Node_Copy(from, newnode, sortClause); - Node_Copy(from, newnode, rtable); - Node_Copy(from, newnode, targetList); - Node_Copy(from, newnode, qual); - - Node_Copy(from, newnode, groupClause); - Node_Copy(from, newnode, havingQual); - newnode->hasAggs = from->hasAggs; newnode->hasSubLinks = from->hasSubLinks; - if (from->unionClause) - { - List *ulist, - *temp_list = NIL; + Node_Copy(from, newnode, rtable); + Node_Copy(from, newnode, targetList); + Node_Copy(from, newnode, qual); + Node_Copy(from, newnode, rowMark); - foreach(ulist, from->unionClause) - temp_list = lappend(temp_list, copyObject(lfirst(ulist))); - newnode->unionClause = temp_list; - } + if (from->uniqueFlag) + newnode->uniqueFlag = pstrdup(from->uniqueFlag); + Node_Copy(from, newnode, sortClause); + Node_Copy(from, newnode, groupClause); + Node_Copy(from, newnode, havingQual); + + /* why is intersectClause missing? */ + Node_Copy(from, newnode, unionClause); Node_Copy(from, newnode, limitOffset); Node_Copy(from, newnode, limitCount); - Node_Copy(from, newnode, rowMark); + /* we do not copy the planner internal fields: base_rel_list, + * join_rel_list, query_pathkeys. Not entirely clear if this is right? + */ return newnode; } diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index 1c53fe6aed..d8538a4506 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.47 1999/08/16 02:17:41 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.48 1999/08/21 03:48:57 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -34,14 +34,23 @@ _equalResdom(Resdom *a, Resdom *b) return false; if (a->restypmod != b->restypmod) return false; - if (strcmp(a->resname, b->resname) != 0) + if (a->resname && b->resname) + { + if (strcmp(a->resname, b->resname) != 0) + return false; + } + else + { + /* must both be null to be equal */ + if (a->resname != b->resname) + return false; + } + if (a->ressortgroupref != b->ressortgroupref) return false; if (a->reskey != b->reskey) return false; if (a->reskeyop != b->reskeyop) return false; - if (a->resgroupref != b->resgroupref) - return false; /* we ignore resjunk flag ... is this correct? */ return true; @@ -208,10 +217,9 @@ _equalAggref(Aggref *a, Aggref *b) return false; if (!equal(a->target, b->target)) return false; - if (a->aggno != b->aggno) - return false; if (a->usenulls != b->usenulls) return false; + /* ignore aggno, which is only a private field for the executor */ return true; } @@ -503,6 +511,14 @@ _equalQuery(Query *a, Query *b) return false; if (a->hasSubLinks != b->hasSubLinks) return false; + if (!equal(a->rtable, b->rtable)) + return false; + if (!equal(a->targetList, b->targetList)) + return false; + if (!equal(a->qual, b->qual)) + return false; + if (!equal(a->rowMark, b->rowMark)) + return false; if (a->uniqueFlag && b->uniqueFlag) { if (strcmp(a->uniqueFlag, b->uniqueFlag) != 0) @@ -515,14 +531,6 @@ _equalQuery(Query *a, Query *b) } if (!equal(a->sortClause, b->sortClause)) return false; - if (!equal(a->rtable, b->rtable)) - return false; - if (!equal(a->targetList, b->targetList)) - return false; - if (!equal(a->qual, b->qual)) - return false; - if (!equal(a->rowMark, b->rowMark)) - return false; if (!equal(a->groupClause, b->groupClause)) return false; if (!equal(a->havingQual, b->havingQual)) @@ -537,9 +545,9 @@ _equalQuery(Query *a, Query *b) return false; /* - * We do not check the internal-to-the-planner fields base_rel_list - * and join_rel_list. They might not be set yet, and in any case they - * should be derivable from the other fields. + * We do not check the internal-to-the-planner fields: base_rel_list, + * join_rel_list, query_pathkeys. They might not be set yet, and + * in any case they should be derivable from the other fields. */ return true; } diff --git a/src/backend/nodes/freefuncs.c b/src/backend/nodes/freefuncs.c index 11f92b28d0..cad49be76b 100644 --- a/src/backend/nodes/freefuncs.c +++ b/src/backend/nodes/freefuncs.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/nodes/Attic/freefuncs.c,v 1.25 1999/08/16 02:17:42 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/nodes/Attic/freefuncs.c,v 1.26 1999/08/21 03:48:57 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -371,8 +371,6 @@ _freeAgg(Agg *node) { FreePlanFields((Plan *) node); - freeList(node->aggs); - pfree(node); } @@ -964,8 +962,6 @@ _freeRowMark(RowMark *node) static void _freeSortClause(SortClause *node) { - freeObject(node->resdom); - pfree(node); } @@ -1000,19 +996,22 @@ _freeQuery(Query *node) } if (node->into) pfree(node->into); + freeObject(node->rtable); + freeObject(node->targetList); + freeObject(node->qual); + freeObject(node->rowMark); if (node->uniqueFlag) pfree(node->uniqueFlag); freeObject(node->sortClause); - freeObject(node->rtable); - freeObject(node->targetList); - freeObject(node->qual); freeObject(node->groupClause); freeObject(node->havingQual); + /* why not intersectClause? */ freeObject(node->unionClause); freeObject(node->limitOffset); freeObject(node->limitCount); - freeObject(node->rowMark); + + /* XXX should we be freeing the planner internal fields? */ pfree(node); } diff --git a/src/backend/nodes/makefuncs.c b/src/backend/nodes/makefuncs.c index d64fdd8c46..26ebed1d45 100644 --- a/src/backend/nodes/makefuncs.c +++ b/src/backend/nodes/makefuncs.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/nodes/makefuncs.c,v 1.16 1999/07/15 22:39:17 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/nodes/makefuncs.c,v 1.17 1999/08/21 03:48:58 tgl Exp $ * * NOTES * Creator functions in POSTGRES 4.2 are generated automatically. Most of @@ -102,9 +102,12 @@ makeResdom(AttrNumber resno, resdom->restype = restype; resdom->restypmod = restypmod; resdom->resname = resname; + /* For historical reasons, ressortgroupref defaults to 0 while + * reskey/reskeyop are passed in explicitly. This is pretty silly. + */ + resdom->ressortgroupref = 0; resdom->reskey = reskey; resdom->reskeyop = reskeyop; - resdom->resgroupref = 0; resdom->resjunk = resjunk; return resdom; } diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index 39b0625d93..226dbb1cef 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -5,7 +5,7 @@ * * Copyright (c) 1994, Regents of the University of California * - * $Id: outfuncs.c,v 1.93 1999/08/16 02:17:42 tgl Exp $ + * $Id: outfuncs.c,v 1.94 1999/08/21 03:48:58 tgl Exp $ * * NOTES * Every (plan) node in POSTGRES has an associated "out" routine which @@ -237,18 +237,15 @@ _outQuery(StringInfo str, Query *node) static void _outSortClause(StringInfo str, SortClause *node) { - appendStringInfo(str, " SORTCLAUSE :resdom "); - _outNode(str, node->resdom); - - appendStringInfo(str, " :opoid %u ", node->opoid); + appendStringInfo(str, " SORTCLAUSE :tleSortGroupRef %d :sortop %u ", + node->tleSortGroupRef, node->sortop); } static void _outGroupClause(StringInfo str, GroupClause *node) { - appendStringInfo(str, " GROUPCLAUSE :grpOpoid %u :tleGroupref %d", - node->grpOpoid, - node->tleGroupref); + appendStringInfo(str, " GROUPCLAUSE :tleSortGroupRef %d :sortop %u ", + node->tleSortGroupRef, node->sortop); } /* @@ -482,9 +479,6 @@ _outAgg(StringInfo str, Agg *node) appendStringInfo(str, " AGG "); _outPlanInfo(str, (Plan *) node); - - appendStringInfo(str, " :aggs "); - _outNode(str, node->aggs); } static void @@ -549,8 +543,8 @@ _outResdom(StringInfo str, Resdom *node) node->reskey, node->reskeyop); - appendStringInfo(str, " :resgroupref %d :resjunk %s ", - node->resgroupref, + appendStringInfo(str, " :ressortgroupref %d :resjunk %s ", + node->ressortgroupref, node->resjunk ? "true" : "false"); } @@ -665,8 +659,7 @@ _outAggref(StringInfo str, Aggref *node) node->aggtype); _outNode(str, node->target); - appendStringInfo(str, ":aggno %d :usenulls %s", - node->aggno, + appendStringInfo(str, " :usenulls %s ", node->usenulls ? "true" : "false"); } diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c index 98385dd96f..588528daa1 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.72 1999/08/16 02:17:43 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.73 1999/08/21 03:48:58 tgl Exp $ * * NOTES * Most of the read functions for plan nodes are tested. (In fact, they @@ -184,12 +184,13 @@ _readSortClause() local_node = makeNode(SortClause); - token = lsptok(NULL, &length); /* skip the :resdom */ - local_node->resdom = nodeRead(true); + token = lsptok(NULL, &length); /* skip :tleSortGroupRef */ + token = lsptok(NULL, &length); /* get tleSortGroupRef */ + local_node->tleSortGroupRef = strtoul(token, NULL, 10); - token = lsptok(NULL, &length); /* skip :opoid */ - token = lsptok(NULL, &length); /* get opoid */ - local_node->opoid = strtoul(token, NULL, 10); + token = lsptok(NULL, &length); /* skip :sortop */ + token = lsptok(NULL, &length); /* get sortop */ + local_node->sortop = strtoul(token, NULL, 10); return local_node; } @@ -207,13 +208,13 @@ _readGroupClause() local_node = makeNode(GroupClause); - token = lsptok(NULL, &length); /* skip :grpOpoid */ - token = lsptok(NULL, &length); /* get grpOpoid */ - local_node->grpOpoid = strtoul(token, NULL, 10); + token = lsptok(NULL, &length); /* skip :tleSortGroupRef */ + token = lsptok(NULL, &length); /* get tleSortGroupRef */ + local_node->tleSortGroupRef = strtoul(token, NULL, 10); - token = lsptok(NULL, &length); /* skip :tleGroupref */ - token = lsptok(NULL, &length); /* get tleGroupref */ - local_node->tleGroupref = strtoul(token, NULL, 10); + token = lsptok(NULL, &length); /* skip :sortop */ + token = lsptok(NULL, &length); /* get sortop */ + local_node->sortop = strtoul(token, NULL, 10); return local_node; } @@ -600,15 +601,10 @@ static Agg * _readAgg() { Agg *local_node; - char *token; - int length; local_node = makeNode(Agg); _getPlan((Plan *) local_node); - token = lsptok(NULL, &length); /* eat :agg */ - local_node->aggs = nodeRead(true); /* now read it */ - return local_node; } @@ -712,9 +708,9 @@ _readResdom() token = lsptok(NULL, &length); /* get reskeyop */ local_node->reskeyop = (Oid) atol(token); - token = lsptok(NULL, &length); /* eat :resgroupref */ - token = lsptok(NULL, &length); /* get resgroupref */ - local_node->resgroupref = strtoul(token, NULL, 10); + token = lsptok(NULL, &length); /* eat :ressortgroupref */ + token = lsptok(NULL, &length); /* get ressortgroupref */ + local_node->ressortgroupref = strtoul(token, NULL, 10); token = lsptok(NULL, &length); /* eat :resjunk */ token = lsptok(NULL, &length); /* get resjunk */ @@ -1163,10 +1159,6 @@ _readAggref() token = lsptok(NULL, &length); /* eat :target */ local_node->target = nodeRead(true); /* now read it */ - token = lsptok(NULL, &length); /* eat :aggno */ - token = lsptok(NULL, &length); /* get aggno */ - local_node->aggno = atoi(token); - token = lsptok(NULL, &length); /* eat :usenulls */ token = lsptok(NULL, &length); /* get usenulls */ local_node->usenulls = (token[0] == 't') ? true : false; diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c index 99ce241fed..6af480629e 100644 --- a/src/backend/optimizer/path/indxpath.c +++ b/src/backend/optimizer/path/indxpath.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.69 1999/08/16 02:17:50 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.70 1999/08/21 03:49:00 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -70,6 +70,8 @@ static List *index_innerjoin(Query *root, RelOptInfo *rel, RelOptInfo *index, List *clausegroup_list, List *outerrelids_list); static bool useful_for_mergejoin(RelOptInfo *rel, RelOptInfo *index, List *joininfo_list); +static bool useful_for_ordering(Query *root, RelOptInfo *rel, + RelOptInfo *index); static bool match_index_to_operand(int indexkey, Var *operand, RelOptInfo *rel, RelOptInfo *index); static bool function_index_operand(Expr *funcOpnd, RelOptInfo *rel, RelOptInfo *index); @@ -86,7 +88,8 @@ static List *prefix_quals(Var *leftop, Oid expr_op, * Generate all interesting index paths for the given relation. * * To be considered for an index scan, an index must match one or more - * restriction clauses or join clauses from the query's qual condition. + * restriction clauses or join clauses from the query's qual condition, + * or match the query's ORDER BY condition. * * There are two basic kinds of index scans. A "plain" index scan uses * only restriction clauses (possibly none at all) in its indexqual, @@ -104,14 +107,6 @@ static List *prefix_quals(Var *leftop, Oid expr_op, * relations. The innerjoin paths are *not* in the return list, but are * appended to the "innerjoin" list of the relation itself. * - * XXX An index scan might also be used simply to order the result. We - * probably should create an index path for any index that matches the - * query's ORDER BY condition, even if it doesn't seem useful for join - * or restriction clauses. But currently, such a path would never - * survive the path selection process, so there's no point. The selection - * process needs to award bonus scores to indexscans that produce a - * suitably-ordered result... - * * 'rel' is the relation for which we want to generate index paths * 'indices' is a list of available indexes for 'rel' * 'restrictinfo_list' is a list of restrictinfo nodes for 'rel' @@ -192,13 +187,16 @@ create_index_paths(Query *root, * index path for it even if there were no restriction clauses. * (If there were, there is no need to make another index path.) * This will allow the index to be considered as a base for a - * mergejoin in later processing. + * mergejoin in later processing. Similarly, if the index matches + * the ordering that is needed for the overall query result, make + * an index path for it even if there is no other reason to do so. */ - if (restrictclauses == NIL && - useful_for_mergejoin(rel, index, joininfo_list)) + if (restrictclauses == NIL) { - retval = lappend(retval, - create_index_path(root, rel, index, NIL)); + if (useful_for_mergejoin(rel, index, joininfo_list) || + useful_for_ordering(root, rel, index)) + retval = lappend(retval, + create_index_path(root, rel, index, NIL)); } /* @@ -748,6 +746,101 @@ indexable_operator(Expr *clause, int xclass, Oid relam, return false; } +/* + * useful_for_mergejoin + * Determine whether the given index can support a mergejoin based + * on any available join clause. + * + * We look to see whether the first indexkey of the index matches the + * left or right sides of any of the mergejoinable clauses and provides + * the ordering needed for that side. If so, the index is useful. + * Matching a second or later indexkey is not useful unless there is + * also a mergeclause for the first indexkey, so we need not consider + * secondary indexkeys at this stage. + * + * 'rel' is the relation for which 'index' is defined + * 'joininfo_list' is the list of JoinInfo nodes for 'rel' + */ +static bool +useful_for_mergejoin(RelOptInfo *rel, + RelOptInfo *index, + List *joininfo_list) +{ + int *indexkeys = index->indexkeys; + Oid *ordering = index->ordering; + List *i; + + if (!indexkeys || indexkeys[0] == 0 || + !ordering || ordering[0] == InvalidOid) + return false; /* unordered index is not useful */ + + foreach(i, joininfo_list) + { + JoinInfo *joininfo = (JoinInfo *) lfirst(i); + List *j; + + foreach(j, joininfo->jinfo_restrictinfo) + { + RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(j); + + if (restrictinfo->mergejoinoperator) + { + if (restrictinfo->left_sortop == ordering[0] && + match_index_to_operand(indexkeys[0], + get_leftop(restrictinfo->clause), + rel, index)) + return true; + if (restrictinfo->right_sortop == ordering[0] && + match_index_to_operand(indexkeys[0], + get_rightop(restrictinfo->clause), + rel, index)) + return true; + } + } + } + return false; +} + +/* + * useful_for_ordering + * Determine whether the given index can produce an ordering matching + * the order that is wanted for the query result. + * + * We check to see whether either forward or backward scan direction can + * match the specified pathkeys. + * + * 'rel' is the relation for which 'index' is defined + */ +static bool +useful_for_ordering(Query *root, + RelOptInfo *rel, + RelOptInfo *index) +{ + List *index_pathkeys; + + if (root->query_pathkeys == NIL) + return false; /* no special ordering requested */ + + index_pathkeys = build_index_pathkeys(root, rel, index); + + if (index_pathkeys == NIL) + return false; /* unordered index */ + + if (pathkeys_contained_in(root->query_pathkeys, index_pathkeys)) + return true; + + /* caution: commute_pathkeys destructively modifies its argument; + * safe because we just built the index_pathkeys for local use here. + */ + if (commute_pathkeys(index_pathkeys)) + { + if (pathkeys_contained_in(root->query_pathkeys, index_pathkeys)) + return true; /* useful as a reverse-order path */ + } + + return false; +} + /**************************************************************************** * ---- ROUTINES TO DO PARTIAL INDEX PREDICATE TESTS ---- ****************************************************************************/ @@ -1285,61 +1378,6 @@ index_innerjoin(Query *root, RelOptInfo *rel, RelOptInfo *index, return path_list; } -/* - * useful_for_mergejoin - * Determine whether the given index can support a mergejoin based - * on any available join clause. - * - * We look to see whether the first indexkey of the index matches the - * left or right sides of any of the mergejoinable clauses and provides - * the ordering needed for that side. If so, the index is useful. - * Matching a second or later indexkey is not useful unless there is - * also a mergeclause for the first indexkey, so we need not consider - * secondary indexkeys at this stage. - * - * 'rel' is the relation for which 'index' is defined - * 'joininfo_list' is the list of JoinInfo nodes for 'rel' - */ -static bool -useful_for_mergejoin(RelOptInfo *rel, - RelOptInfo *index, - List *joininfo_list) -{ - int *indexkeys = index->indexkeys; - Oid *ordering = index->ordering; - List *i; - - if (!indexkeys || indexkeys[0] == 0 || - !ordering || ordering[0] == InvalidOid) - return false; /* unordered index is not useful */ - - foreach(i, joininfo_list) - { - JoinInfo *joininfo = (JoinInfo *) lfirst(i); - List *j; - - foreach(j, joininfo->jinfo_restrictinfo) - { - RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(j); - - if (restrictinfo->mergejoinoperator) - { - if (restrictinfo->left_sortop == ordering[0] && - match_index_to_operand(indexkeys[0], - get_leftop(restrictinfo->clause), - rel, index)) - return true; - if (restrictinfo->right_sortop == ordering[0] && - match_index_to_operand(indexkeys[0], - get_rightop(restrictinfo->clause), - rel, index)) - return true; - } - } - } - return false; -} - /**************************************************************************** * ---- ROUTINES TO CHECK OPERANDS ---- ****************************************************************************/ diff --git a/src/backend/optimizer/path/joinpath.c b/src/backend/optimizer/path/joinpath.c index 55891d87a9..c1ac6a2c4d 100644 --- a/src/backend/optimizer/path/joinpath.c +++ b/src/backend/optimizer/path/joinpath.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/path/joinpath.c,v 1.45 1999/08/16 02:17:51 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/path/joinpath.c,v 1.46 1999/08/21 03:49:00 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -408,7 +408,8 @@ match_unsorted_outer(RelOptInfo *joinrel, trialinnerpath = get_cheapest_path_for_pathkeys(innerrel->pathlist, ltruncate(clausecount, - trialsortkeys)); + trialsortkeys), + false); if (trialinnerpath != NULL && trialinnerpath->path_cost < cheapest_cost) { @@ -488,7 +489,8 @@ match_unsorted_inner(RelOptInfo *joinrel, /* Look for an outer path already ordered well enough to merge */ mergeouterpath = get_cheapest_path_for_pathkeys(outerrel->pathlist, - outersortkeys); + outersortkeys, + false); /* Should we use the mergeouter, or sort the cheapest outer? */ if (mergeouterpath != NULL && diff --git a/src/backend/optimizer/path/pathkeys.c b/src/backend/optimizer/path/pathkeys.c index 44a7b614b6..41a3ff35b4 100644 --- a/src/backend/optimizer/path/pathkeys.c +++ b/src/backend/optimizer/path/pathkeys.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/path/pathkeys.c,v 1.14 1999/08/16 02:17:52 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/path/pathkeys.c,v 1.15 1999/08/21 03:49:01 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -20,6 +20,7 @@ #include "optimizer/tlist.h" #include "optimizer/var.h" #include "parser/parsetree.h" +#include "parser/parse_func.h" #include "utils/lsyscache.h" static PathKeyItem *makePathKeyItem(Node *key, Oid sortop); @@ -89,6 +90,11 @@ static List *build_join_pathkey(List *pathkeys, List *join_rel_tlist, * executor might have to split the join into multiple batches. Therefore * a Hashjoin is always given NIL pathkeys. * + * Pathkeys are also useful to represent an ordering that we wish to achieve, + * since they are easily compared to the pathkeys of a potential candidate + * path. So, SortClause lists are turned into pathkeys lists for use inside + * the optimizer. + * * -- bjm & tgl *-------------------- */ @@ -254,9 +260,11 @@ pathkeys_contained_in(List *keys1, List *keys2) * * 'paths' is a list of possible paths (either inner or outer) * 'pathkeys' represents a required ordering + * if 'indexpaths_only' is true, only IndexPaths will be considered. */ Path * -get_cheapest_path_for_pathkeys(List *paths, List *pathkeys) +get_cheapest_path_for_pathkeys(List *paths, List *pathkeys, + bool indexpaths_only) { Path *matched_path = NULL; List *i; @@ -265,6 +273,9 @@ get_cheapest_path_for_pathkeys(List *paths, List *pathkeys) { Path *path = (Path *) lfirst(i); + if (indexpaths_only && ! IsA(path, IndexPath)) + continue; + if (pathkeys_contained_in(pathkeys, path->pathkeys)) { if (matched_path == NULL || @@ -314,7 +325,8 @@ build_index_pathkeys(Query *root, RelOptInfo *rel, RelOptInfo *index) funcnode->funcisindex = false; funcnode->funcsize = 0; funcnode->func_fcache = NULL; - funcnode->func_tlist = NIL; + /* we assume here that the function returns a base type... */ + funcnode->func_tlist = setup_base_tlist(funcnode->functype); funcnode->func_planlist = NIL; while (*indexkeys != 0) @@ -516,6 +528,70 @@ build_join_pathkey(List *pathkey, return new_pathkey; } +/* + * commute_pathkeys + * Attempt to commute the operators in a set of pathkeys, producing + * pathkeys that describe the reverse sort order (DESC instead of ASC). + * Returns TRUE if successful (all the operators have commutators). + * + * CAUTION: given pathkeys are modified in place, even if not successful!! + * Usually, caller should have just built or copied the pathkeys list to + * ensure there are no unwanted side-effects. + */ +bool +commute_pathkeys(List *pathkeys) +{ + List *i; + + foreach(i, pathkeys) + { + List *pathkey = lfirst(i); + List *j; + + foreach(j, pathkey) + { + PathKeyItem *key = lfirst(j); + + key->sortop = get_commutator(key->sortop); + if (key->sortop == InvalidOid) + return false; + } + } + return true; /* successful */ +} + +/**************************************************************************** + * PATHKEYS AND SORT CLAUSES + ****************************************************************************/ + +/* + * make_pathkeys_for_sortclauses + * Generate a pathkeys list that represents the sort order specified + * by a list of SortClauses (GroupClauses will work too!) + * + * 'sortclauses' is a list of SortClause or GroupClause nodes + * 'tlist' is the targetlist to find the referenced tlist entries in + */ +List * +make_pathkeys_for_sortclauses(List *sortclauses, List *tlist) +{ + List *pathkeys = NIL; + List *i; + + foreach(i, sortclauses) + { + SortClause *sortcl = (SortClause *) lfirst(i); + Node *sortkey; + PathKeyItem *pathkey; + + sortkey = get_sortgroupclause_expr(sortcl, tlist); + pathkey = makePathKeyItem(sortkey, sortcl->sortop); + /* pathkey becomes a one-element sublist */ + pathkeys = lappend(pathkeys, lcons(pathkey, NIL)); + } + return pathkeys; +} + /**************************************************************************** * PATHKEYS AND MERGECLAUSES ****************************************************************************/ diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c index 40923fe27d..d1f756fc7c 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.73 1999/08/18 04:15:16 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.74 1999/08/21 03:49:02 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1131,7 +1131,6 @@ make_agg(List *tlist, Plan *lefttree) node->plan.targetlist = tlist; node->plan.lefttree = lefttree; node->plan.righttree = (Plan *) NULL; - node->aggs = NIL; return node; } @@ -1141,15 +1140,15 @@ make_group(List *tlist, bool tuplePerGroup, int ngrp, AttrNumber *grpColIdx, - Sort *lefttree) + Plan *lefttree) { Group *node = makeNode(Group); - copy_costsize(&node->plan, (Plan *) lefttree); + copy_costsize(&node->plan, lefttree); node->plan.state = (EState *) NULL; node->plan.qual = NULL; node->plan.targetlist = tlist; - node->plan.lefttree = (Plan *) lefttree; + node->plan.lefttree = lefttree; node->plan.righttree = (Plan *) NULL; node->tuplePerGroup = tuplePerGroup; node->numCols = ngrp; diff --git a/src/backend/optimizer/plan/planmain.c b/src/backend/optimizer/plan/planmain.c index 76e010c9d2..f6f62abfe0 100644 --- a/src/backend/optimizer/plan/planmain.c +++ b/src/backend/optimizer/plan/planmain.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planmain.c,v 1.40 1999/07/16 04:59:19 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planmain.c,v 1.41 1999/08/21 03:49:03 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -17,11 +17,13 @@ #include "optimizer/clauses.h" +#include "optimizer/cost.h" #include "optimizer/paths.h" #include "optimizer/planmain.h" #include "optimizer/prep.h" #include "optimizer/subselect.h" #include "optimizer/tlist.h" +#include "utils/lsyscache.h" static Plan *subplanner(Query *root, List *flat_tlist, List *qual); @@ -42,6 +44,13 @@ static Result *make_result(List *tlist, Node *resconstantqual, Plan *subplan); * tlist is the target list of the query * qual is the qualification of the query * + * Note: the Query node now also includes a query_pathkeys field, which + * signals query_planner that the indicated sort order is wanted in the + * final output plan. If, for some reason, query_planner is unable to + * comply, it sets query_pathkeys to NIL before returning. (The reason + * query_pathkeys is a Query field and not a passed parameter is that + * the low-level routines in indxpath.c need to see it.) + * * Returns a query plan. */ Plan * @@ -100,6 +109,8 @@ query_planner(Query *root, */ if (var_only_tlist == NULL && qual == NULL) { + root->query_pathkeys = NIL; /* these plans make unordered results */ + switch (command_type) { case CMD_SELECT: @@ -152,6 +163,8 @@ query_planner(Query *root, */ set_tlist_references(subplan); + root->query_pathkeys = NIL; /* result is unordered, no? */ + return subplan; } @@ -163,15 +176,15 @@ query_planner(Query *root, * responsibility to optimally push these expressions down the plan * tree. -- Wei * - * Note: formerly there was a test here to skip the flatten call if we - * expected union_planner to insert a Group or Agg node above our + * Note: formerly there was a test here to skip the unflatten call if + * we expected union_planner to insert a Group or Agg node above our * result. However, now union_planner tells us exactly what it wants * returned, and we just do it. Much cleaner. */ else { - subplan->targetlist = flatten_tlist_vars(tlist, - subplan->targetlist); + subplan->targetlist = unflatten_tlist(tlist, + subplan->targetlist); return subplan; } @@ -204,6 +217,8 @@ subplanner(Query *root, List *qual) { RelOptInfo *final_rel; + Cost cheapest_cost; + Path *sortedpath; /* * Initialize the targetlist and qualification, adding entries to @@ -244,18 +259,83 @@ subplanner(Query *root, } #endif - /* - * Determine the cheapest path and create a subplan corresponding to - * it. - */ - if (final_rel) - return create_plan((Path *) final_rel->cheapestpath); - else + if (! final_rel) { elog(NOTICE, "final relation is null"); + root->query_pathkeys = NIL; /* result is unordered, no? */ return create_plan((Path *) NULL); } + /* + * Determine the cheapest path and create a subplan to execute it. + * + * If no special sort order is wanted, or if the cheapest path is + * already appropriately ordered, just use the cheapest path. + */ + if (root->query_pathkeys == NIL || + pathkeys_contained_in(root->query_pathkeys, + final_rel->cheapestpath->pathkeys)) + return create_plan(final_rel->cheapestpath); + /* + * Otherwise, look to see if we have an already-ordered path that is + * cheaper than doing an explicit sort on cheapestpath. + */ + cheapest_cost = final_rel->cheapestpath->path_cost + + cost_sort(root->query_pathkeys, final_rel->size, final_rel->width); + + sortedpath = get_cheapest_path_for_pathkeys(final_rel->pathlist, + root->query_pathkeys, + false); + if (sortedpath) + { + if (sortedpath->path_cost <= cheapest_cost) + { + /* Found a better presorted path, use it */ + return create_plan(sortedpath); + } + /* otherwise, doing it the hard way is still cheaper */ + } + else + { + /* + * If we found no usable presorted path at all, it is possible + * that the user asked for descending sort order. Check to see + * if we can satisfy the pathkeys by using a backwards indexscan. + * To do this, we commute all the operators in the pathkeys and + * then look for a matching path that is an IndexPath. + */ + List *commuted_pathkeys = copyObject(root->query_pathkeys); + + if (commute_pathkeys(commuted_pathkeys)) + { + /* pass 'true' to force only IndexPaths to be considered */ + sortedpath = get_cheapest_path_for_pathkeys(final_rel->pathlist, + commuted_pathkeys, + true); + if (sortedpath && sortedpath->path_cost <= cheapest_cost) + { + /* + * Kluge here: since IndexPath has no representation for + * backwards scan, we have to convert to Plan format and + * then poke the result. + */ + Plan *sortedplan = create_plan(sortedpath); + + Assert(IsA(sortedplan, IndexScan)); + ((IndexScan *) sortedplan)->indxorderdir = BackwardScanDirection; + return sortedplan; + } + } + } + + /* Nothing for it but to sort the cheapestpath... + * + * we indicate we failed to sort the plan, and let the caller + * stick the appropriate sortplan on top. + */ + root->query_pathkeys = NIL; /* sorry, it ain't sorted */ + + return create_plan(final_rel->cheapestpath); } /***************************************************************************** diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index 17ad449839..b328c40f22 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.62 1999/08/09 06:20:26 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.63 1999/08/21 03:49:03 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -22,6 +22,7 @@ #include "nodes/makefuncs.h" #include "optimizer/clauses.h" #include "optimizer/internal.h" +#include "optimizer/paths.h" #include "optimizer/planmain.h" #include "optimizer/planner.h" #include "optimizer/prep.h" @@ -35,11 +36,10 @@ #include "utils/syscache.h" static List *make_subplanTargetList(Query *parse, List *tlist, - AttrNumber **groupColIdx); + AttrNumber **groupColIdx); static Plan *make_groupplan(List *group_tlist, bool tuplePerGroup, - List *groupClause, AttrNumber *grpColIdx, - Plan *subplan); -static ScanDirection get_dir_to_omit_sortplan(List *sortcls, Plan *plan); + List *groupClause, AttrNumber *grpColIdx, + bool is_sorted, Plan *subplan); static Plan *make_sortplan(List *tlist, List *sortcls, Plan *plannode); /***************************************************************************** @@ -59,6 +59,7 @@ planner(Query *parse) PlannerPlanId = 0; transformKeySetQuery(parse); + result_plan = union_planner(parse); Assert(PlannerQueryLevel == 1); @@ -88,6 +89,7 @@ union_planner(Query *parse) List *rangetable = parse->rtable; Plan *result_plan = (Plan *) NULL; AttrNumber *groupColIdx = NULL; + bool is_sorted = false; Index rt_index; if (parse->unionClause) @@ -179,6 +181,34 @@ union_planner(Query *parse) } } + /* + * Figure out whether we need a sorted result from query_planner. + * + * If we have a GROUP BY clause, then we want a result sorted + * properly for grouping. Otherwise, if there is an ORDER BY clause + * and no need for an aggregate node, we want to sort by the ORDER BY + * clause. (XXX In some cases, we could presort even when there is + * an aggregate, but I'll leave that refinement for another day.) + * + * NOTE: the reason we put the target pathkeys into the Query node + * rather than passing them as an argument to query_planner is that + * the low-level routines in indxpath.c want to be able to see them. + */ + if (parse->groupClause) + { + parse->query_pathkeys = + make_pathkeys_for_sortclauses(parse->groupClause, tlist); + } + else if (parse->sortClause && ! parse->hasAggs) + { + parse->query_pathkeys = + make_pathkeys_for_sortclauses(parse->sortClause, tlist); + } + else + { + parse->query_pathkeys = NIL; + } + /* * Generate appropriate target list for subplan; may be different * from tlist if grouping or aggregation is needed. @@ -190,6 +220,12 @@ union_planner(Query *parse) parse->commandType, sub_tlist, (List *) parse->qual); + + /* query_planner sets query_pathkeys to NIL if it didn't make + * a properly sorted plan + */ + if (parse->query_pathkeys) + is_sorted = true; } /* query_planner returns NULL if it thinks plan is bogus */ @@ -197,8 +233,8 @@ union_planner(Query *parse) elog(ERROR, "union_planner: failed to create plan"); /* - * If we have a GROUP BY clause, insert a group node (with the - * appropriate sort node.) + * If we have a GROUP BY clause, insert a group node (plus the + * appropriate sort node, if necessary). */ if (parse->groupClause) { @@ -215,17 +251,27 @@ union_planner(Query *parse) /* * If there are aggregates then the Group node should just return - * the same (simplified) tlist as the subplan, which we indicate - * to make_groupplan by passing NIL. If there are no aggregates + * the same set of vars as the subplan did (but we can exclude + * any GROUP BY expressions). If there are no aggregates * then the Group node had better compute the final tlist. */ - group_tlist = parse->hasAggs ? NIL : tlist; + if (parse->hasAggs) + group_tlist = flatten_tlist(result_plan->targetlist); + else + group_tlist = tlist; result_plan = make_groupplan(group_tlist, tuplePerGroup, parse->groupClause, groupColIdx, + is_sorted, result_plan); + + /* + * Assume the result of the group step is not ordered suitably + * for any ORDER BY that may exist. XXX it might be; improve this! + */ + is_sorted = false; } /* @@ -269,66 +315,48 @@ union_planner(Query *parse) result_plan->qual = (List *) parse->havingQual; /* - * Update vars to refer to subplan result tuples, find Aggrefs, + * Update vars to refer to subplan result tuples, and * make sure there is an Aggref in every HAVING clause. */ if (!set_agg_tlist_references((Agg *) result_plan)) elog(ERROR, "SELECT/HAVING requires aggregates to be valid"); /* - * Check that we actually found some aggregates, else executor - * will die unpleasantly. (This defends against possible bugs in - * parser or rewrite that might cause hasAggs to be incorrectly - * set 'true'. It's not easy to recover here, since we've already - * made decisions assuming there will be an Agg node.) + * Assume result is not ordered suitably for ORDER BY. + * XXX it might be; improve this! */ - if (((Agg *) result_plan)->aggs == NIL) - elog(ERROR, "union_planner: query is marked hasAggs, but I don't see any"); + is_sorted = false; } /* - * For now, before we hand back the plan, check to see if there is a - * user-specified sort that needs to be done. Eventually, this will - * be moved into the guts of the planner s.t. user specified sorts - * will be considered as part of the planning process. Since we can - * only make use of user-specified sorts in special cases, we can do - * the optimization step later. + * If we were not able to make the plan come out in the right order, + * add an explicit sort step. */ + if (parse->sortClause && ! is_sorted) + { + result_plan = make_sortplan(tlist, parse->sortClause, result_plan); + } + /* + * Finally, if there is a UNIQUE clause, add the UNIQUE node. + */ if (parse->uniqueFlag) { - Plan *sortplan = make_sortplan(tlist, parse->sortClause, result_plan); - - return ((Plan *) make_unique(tlist, sortplan, parse->uniqueFlag)); - } - else - { - if (parse->sortClause) - { - ScanDirection dir = get_dir_to_omit_sortplan(parse->sortClause, result_plan); - if (ScanDirectionIsNoMovement(dir)) - return (make_sortplan(tlist, parse->sortClause, result_plan)); - else - { - ((IndexScan *)result_plan)->indxorderdir = dir; - return ((Plan *) result_plan); - } - } - else - return ((Plan *) result_plan); + result_plan = (Plan *) make_unique(tlist, result_plan, + parse->uniqueFlag); } + return result_plan; } /*--------------- * make_subplanTargetList - * Generate appropriate target lists when grouping is required. + * Generate appropriate target list when grouping is required. * - * When union_planner inserts Aggregate and/or Group/Sort plan nodes above - * the result of query_planner, we typically need to pass a different + * When union_planner inserts Aggregate and/or Group plan nodes above + * the result of query_planner, we typically want to pass a different * target list to query_planner than the outer plan nodes should have. - * This routine generates the correct target list for the subplan, and - * if necessary modifies the target list for the inserted nodes as well. + * This routine generates the correct target list for the subplan. * * The initial target list passed from the parser already contains entries * for all ORDER BY and GROUP BY expressions, but it will not have entries @@ -340,26 +368,18 @@ union_planner(Query *parse) * given a query like * SELECT a+b,SUM(c+d) FROM table GROUP BY a+b; * we want to pass this targetlist to the subplan: - * a+b,c,d + * a,b,c,d,a+b * where the a+b target will be used by the Sort/Group steps, and the - * c and d targets will be needed to compute the aggregate results. + * other targets will be used for computing the final results. (In the + * above example we could theoretically suppress the a and b targets and + * use only a+b, but it's not really worth the trouble.) * * 'parse' is the query being processed. - * 'tlist' is the query's target list. CAUTION: list elements may be - * modified by this routine! + * 'tlist' is the query's target list. * 'groupColIdx' receives an array of column numbers for the GROUP BY * expressions (if there are any) in the subplan's target list. * - * The result is the targetlist to be passed to the subplan. Also, - * the parent tlist is modified so that any nontrivial targetlist items that - * exactly match GROUP BY items are replaced by simple Var nodes referencing - * those outputs of the subplan. This avoids redundant recalculations in - * cases like - * SELECT a+1, ... GROUP BY a+1 - * Note, however, that other varnodes in the parent's targetlist (and - * havingQual, if any) will still need to be updated to refer to outputs - * of the subplan. This routine is quite large enough already, so we do - * that later. + * The result is the targetlist to be passed to the subplan. *--------------- */ static List * @@ -368,14 +388,8 @@ make_subplanTargetList(Query *parse, AttrNumber **groupColIdx) { List *sub_tlist; - List *prnt_tlist; - List *sl, - *gl; - List *glc = NIL; - List *extravars = NIL; + List *extravars; int numCols; - AttrNumber *grpColIdx = NULL; - int next_resno = 1; *groupColIdx = NULL; @@ -387,247 +401,151 @@ make_subplanTargetList(Query *parse, return tlist; /* - * If grouping, make a working copy of groupClause list (which we use - * just to verify that we found all the groupClause items in tlist). - * Also allocate space to remember where the group columns are in the - * subplan tlist. + * Otherwise, start with a "flattened" tlist (having just the vars + * mentioned in the targetlist and HAVING qual). + */ + sub_tlist = flatten_tlist(tlist); + extravars = pull_var_clause(parse->havingQual); + sub_tlist = add_to_flat_tlist(sub_tlist, extravars); + freeList(extravars); + + /* + * If grouping, create sub_tlist entries for all GROUP BY expressions + * (GROUP BY items that are simple Vars should be in the list already), + * and make an array showing where the group columns are in the sub_tlist. */ numCols = length(parse->groupClause); if (numCols > 0) { - glc = listCopy(parse->groupClause); + int keyno = 0; + AttrNumber *grpColIdx; + List *gl; + grpColIdx = (AttrNumber *) palloc(sizeof(AttrNumber) * numCols); *groupColIdx = grpColIdx; - } - - sub_tlist = new_unsorted_tlist(tlist); /* make a modifiable copy */ - - /* - * Step 1: build grpColIdx by finding targetlist items that match - * GroupBy entries. If there are aggregates, remove non-GroupBy items - * from sub_tlist, and reset its resnos accordingly. When we leave an - * expression in the subplan tlist, modify the parent tlist to copy - * the value from the subplan output rather than re-evaluating it. - */ - prnt_tlist = tlist; /* scans parent tlist in sync with sl */ - foreach(sl, sub_tlist) - { - TargetEntry *te = (TargetEntry *) lfirst(sl); - TargetEntry *parentte = (TargetEntry *) lfirst(prnt_tlist); - Resdom *resdom = te->resdom; - bool keepInSubPlan = true; - bool foundGroupClause = false; - int keyno = 0; foreach(gl, parse->groupClause) { - GroupClause *grpcl = (GroupClause *) lfirst(gl); + GroupClause *grpcl = (GroupClause *) lfirst(gl); + Node *groupexpr = get_sortgroupclause_expr(grpcl, tlist); + TargetEntry *te = NULL; + List *sl; - keyno++; /* sort key # for this GroupClause */ - if (grpcl->tleGroupref == resdom->resgroupref) + /* Find or make a matching sub_tlist entry */ + foreach(sl, sub_tlist) { - /* Found a matching groupclause; record info for sorting */ - foundGroupClause = true; - resdom->reskey = keyno; - resdom->reskeyop = get_opcode(grpcl->grpOpoid); - grpColIdx[keyno - 1] = next_resno; - - /* - * Remove groupclause from our list of unmatched - * groupclauses. NB: this depends on having used a shallow - * listCopy() above. - */ - glc = lremove((void *) grpcl, glc); - break; + te = (TargetEntry *) lfirst(sl); + if (equal(groupexpr, te->expr)) + break; } - } - - if (!foundGroupClause) - { - - /* - * Non-GroupBy entry: remove it from subplan if there are - * aggregates in query - it will be evaluated by Aggregate - * plan. But do not remove simple-Var entries; we'd just have - * to add them back anyway, and we risk confusing - * INSERT/UPDATE. - */ - if (parse->hasAggs && !IsA(te->expr, Var)) - keepInSubPlan = false; - } - - if (keepInSubPlan) - { - /* Assign new sequential resnos to subplan tlist items */ - resdom->resno = next_resno++; - if (!IsA(parentte->expr, Var)) + if (! sl) { - - /* - * Since the item is being computed in the subplan, we can - * just make a Var node to reference it in the outer plan, - * rather than recomputing it there. Note we use varnoold - * = -1 as a flag to let replace_vars_with_subplan_refs - * know it needn't change this Var node. If it's only a - * Var anyway, we leave it alone for now; - * replace_vars_with_subplan_refs will fix it later. - */ - parentte->expr = (Node *) makeVar(1, resdom->resno, - resdom->restype, - resdom->restypmod, - 0, -1, resdom->resno); + te = makeTargetEntry(makeResdom(length(sub_tlist) + 1, + exprType(groupexpr), + exprTypmod(groupexpr), + NULL, + (Index) 0, + (Oid) 0, + false), + groupexpr); + sub_tlist = lappend(sub_tlist, te); } - } - else - { - /* - * Remove this tlist item from the subplan, but remember the - * vars it needs. The outer tlist item probably needs - * changes, but that will happen later. - */ - sub_tlist = lremove(te, sub_tlist); - extravars = nconc(extravars, pull_var_clause(te->expr)); - } - - prnt_tlist = lnext(prnt_tlist); - } - - /* We should have found all the GROUP BY clauses in the tlist. */ - if (length(glc) != 0) - elog(ERROR, "make_subplanTargetList: GROUP BY attribute not found in target list"); - - /* - * Add subplan targets for any variables needed by removed tlist - * entries that aren't otherwise mentioned in the subplan target list. - * We'll also need targets for any variables seen only in HAVING. - */ - extravars = nconc(extravars, pull_var_clause(parse->havingQual)); - - foreach(gl, extravars) - { - Var *v = (Var *) lfirst(gl); - - if (tlist_member(v, sub_tlist) == NULL) - { - - /* - * Make sure sub_tlist element is a fresh object not shared - * with any other structure; not sure if anything will break - * if it is shared, but better to be safe... - */ - sub_tlist = lappend(sub_tlist, - create_tl_element((Var *) copyObject(v), - next_resno)); - next_resno++; + /* and save its resno */ + grpColIdx[keyno++] = te->resdom->resno; } } return sub_tlist; } +/* + * make_groupplan + * Add a Group node for GROUP BY processing. + * If we couldn't make the subplan produce presorted output for grouping, + * first add an explicit Sort node. + */ static Plan * make_groupplan(List *group_tlist, bool tuplePerGroup, List *groupClause, AttrNumber *grpColIdx, + bool is_sorted, Plan *subplan) { - List *sort_tlist; - List *sl; - Sort *sortplan; - Group *grpplan; int numCols = length(groupClause); + Group *grpplan; - /* - * Make the targetlist for the Sort node; it always just references - * each of the corresponding target items of the subplan. We need to - * ensure that simple Vars in the subplan's target list are - * recognizable by replace_vars_with_subplan_refs when it's applied to - * the Sort/Group target list, so copy up their varnoold/varoattno. - */ - sort_tlist = NIL; - foreach(sl, subplan->targetlist) + if (! is_sorted) { - TargetEntry *te = (TargetEntry *) lfirst(sl); - Resdom *resdom = te->resdom; - Var *newvar; + /* + * The Sort node always just takes a copy of the subplan's tlist + * plus ordering information. (This might seem inefficient if the + * subplan contains complex GROUP BY expressions, but in fact Sort + * does not evaluate its targetlist --- it only outputs the same + * tuples in a new order. So the expressions we might be copying + * are just dummies with no extra execution cost.) + */ + List *sort_tlist = new_unsorted_tlist(subplan->targetlist); + int keyno = 0; + List *gl; - if (IsA(te->expr, Var)) + foreach(gl, groupClause) { - Var *subvar = (Var *) te->expr; + GroupClause *grpcl = (GroupClause *) lfirst(gl); + TargetEntry *te = nth(grpColIdx[keyno]-1, sort_tlist); + Resdom *resdom = te->resdom; - newvar = makeVar(1, resdom->resno, - resdom->restype, resdom->restypmod, - 0, subvar->varnoold, subvar->varoattno); - } - else - { - newvar = makeVar(1, resdom->resno, - resdom->restype, resdom->restypmod, - 0, -1, resdom->resno); + /* + * Check for the possibility of duplicate group-by clauses --- the + * parser should have removed 'em, but the Sort executor will get + * terribly confused if any get through! + */ + if (resdom->reskey == 0) + { + /* OK, insert the ordering info needed by the executor. */ + resdom->reskey = ++keyno; + resdom->reskeyop = get_opcode(grpcl->sortop); + } } - sort_tlist = lappend(sort_tlist, - makeTargetEntry((Resdom *) copyObject(resdom), - (Node *) newvar)); + subplan = (Plan *) make_sort(sort_tlist, + _NONAME_RELATION_ID_, + subplan, + keyno); } /* - * Make the Sort node + * Fix variables in tlist (should be done somewhere else?) */ - sortplan = make_sort(sort_tlist, - _NONAME_RELATION_ID_, - subplan, - numCols); - sortplan->plan.cost = subplan->cost; /* XXX assume no cost */ - - /* - * If the caller gave us a target list, use it after fixing the - * variables. If not, we need the same sort of "repeater" tlist as for - * the Sort node. - */ - if (group_tlist) - { - group_tlist = copyObject(group_tlist); /* necessary?? */ - replace_tlist_with_subplan_refs(group_tlist, - (Index) 0, - subplan->targetlist); - } - else - group_tlist = copyObject(sort_tlist); + group_tlist = copyObject(group_tlist); /* necessary?? */ + replace_tlist_with_subplan_refs(group_tlist, + (Index) 0, + subplan->targetlist); /* * Make the Group node */ grpplan = make_group(group_tlist, tuplePerGroup, numCols, - grpColIdx, sortplan); + grpColIdx, subplan); return (Plan *) grpplan; } /* * make_sortplan - * Returns a sortplan which is basically a SORT node attached to the - * top of the plan returned from the planner. It also adds the - * cost of sorting into the plan. - * - * sortkeys: ( resdom1 resdom2 resdom3 ...) - * sortops: (sortop1 sortop2 sortop3 ...) + * Add a Sort node to implement an explicit ORDER BY clause. */ static Plan * make_sortplan(List *tlist, List *sortcls, Plan *plannode) { - Plan *sortplan = (Plan *) NULL; - List *temp_tlist = NIL; - List *i = NIL; - Resdom *resnode = (Resdom *) NULL; - Resdom *resdom = (Resdom *) NULL; - int keyno = 1; + List *temp_tlist; + List *i; + int keyno = 0; /* - * First make a copy of the tlist so that we don't corrupt the the - * original . + * First make a copy of the tlist so that we don't corrupt the + * original. */ temp_tlist = new_unsorted_tlist(tlist); @@ -635,35 +553,38 @@ make_sortplan(List *tlist, List *sortcls, Plan *plannode) foreach(i, sortcls) { SortClause *sortcl = (SortClause *) lfirst(i); + Index refnumber = sortcl->tleSortGroupRef; + TargetEntry *tle = NULL; + Resdom *resdom; + List *l; - resnode = sortcl->resdom; - resdom = tlist_resdom(temp_tlist, resnode); + foreach(l, temp_tlist) + { + tle = (TargetEntry *) lfirst(l); + if (tle->resdom->ressortgroupref == refnumber) + break; + } + if (l == NIL) + elog(ERROR, "make_sortplan: ORDER BY expression not found in targetlist"); + resdom = tle->resdom; /* - * Order the resdom keys and replace the operator OID for each key - * with the regproc OID. + * Check for the possibility of duplicate order-by clauses --- the + * parser should have removed 'em, but the executor will get terribly + * confused if any get through! */ - resdom->reskey = keyno; - resdom->reskeyop = get_opcode(sortcl->opoid); - keyno += 1; + if (resdom->reskey == 0) + { + /* OK, insert the ordering info needed by the executor. */ + resdom->reskey = ++keyno; + resdom->reskeyop = get_opcode(sortcl->sortop); + } } - sortplan = (Plan *) make_sort(temp_tlist, - _NONAME_RELATION_ID_, - (Plan *) plannode, - length(sortcls)); - - /* - * XXX Assuming that an internal sort has no. cost. This is wrong, but - * given that at this point, we don't know the no. of tuples returned, - * etc, we can't do better than to add a constant cost. This will be - * fixed once we move the sort further into the planner, but for now - * ... functionality.... - */ - - sortplan->cost = plannode->cost; - - return sortplan; + return (Plan *) make_sort(temp_tlist, + _NONAME_RELATION_ID_, + plannode, + keyno); } /* @@ -828,187 +749,3 @@ pg_checkretval(Oid rettype, List *queryTreeList) /* success */ return; } - - -/* ---------- - * Support function for get scan direction to omit sortplan - * ---------- - */ -static TargetEntry * -get_matching_tle(Plan *plan, Resdom *resdom) -{ - List *i; - TargetEntry *tle; - - foreach(i, plan->targetlist) - { - tle = (TargetEntry *) lfirst(i); - if (tle->resdom->resno == resdom->resno) - return tle; - } - return NULL; -} - - -/* ---------- - * Check if a user requested ORDER BY is already satisfied by - * the choosen index scan. - * - * Returns the direction of Index scan to omit sort, - * if sort is required returns NoMovementScanDirection - * - * ---------- - */ -static ScanDirection -get_dir_to_omit_sortplan(List *sortcls, Plan *plan) -{ - Relation indexRel; - IndexScan *indexScan; - Oid indexId; - List *i; - HeapTuple htup; - Form_pg_index index_tup; - int key_no = 0; - ScanDirection dir, nodir = NoMovementScanDirection; - - dir = nodir; - /* ---------- - * Must be an IndexScan - * ---------- - */ - if (nodeTag(plan) != T_IndexScan) - return nodir; - - indexScan = (IndexScan *) plan; - - /* ---------- - * Should not have left- or righttree - * ---------- - */ - if (plan->lefttree != NULL) - return nodir; - if (plan->righttree != NULL) - return nodir; - - /* ---------- - * Must be a single index scan - * ---------- - */ - if (length(indexScan->indxid) != 1) - return nodir; - - /* ---------- - * Indices can only have up to 8 attributes. So an ORDER BY using - * more that 8 attributes could never be satisfied by an index. - * ---------- - */ - if (length(sortcls) > 8) - return nodir; - - /* ---------- - * The choosen Index must be a btree - * ---------- - */ - indexId = lfirsti(indexScan->indxid); - - indexRel = index_open(indexId); - if (strcmp(nameout(&(indexRel->rd_am->amname)), "btree") != 0) - { - heap_close(indexRel); - return nodir; - } - heap_close(indexRel); - - /* ---------- - * Fetch the index tuple - * ---------- - */ - htup = SearchSysCacheTuple(INDEXRELID, - ObjectIdGetDatum(indexId), 0, 0, 0); - if (!HeapTupleIsValid(htup)) - elog(ERROR, "cache lookup for index %u failed", indexId); - index_tup = (Form_pg_index) GETSTRUCT(htup); - - /* ---------- - * Check if all the sort clauses match the attributes in the index - * ---------- - */ - foreach(i, sortcls) - { - SortClause *sortcl; - Resdom *resdom; - TargetEntry *tle; - Var *var; - - sortcl = (SortClause *) lfirst(i); - - resdom = sortcl->resdom; - tle = get_matching_tle(plan, resdom); - if (tle == NULL) - { - /* ---------- - * Could this happen? - * ---------- - */ - return nodir; - } - if (nodeTag(tle->expr) != T_Var) - { - /* ---------- - * The target list expression isn't a var, so it - * cannot be the indexed attribute - * ---------- - */ - return nodir; - } - var = (Var *) (tle->expr); - - if (var->varno != indexScan->scan.scanrelid) - { - /* ---------- - * This Var isn't from the scan relation. So it isn't - * that of the index - * ---------- - */ - return nodir; - } - - if (var->varattno != index_tup->indkey[key_no]) - { - /* ---------- - * It isn't the indexed attribute. - * ---------- - */ - return nodir; - } - - if (oprid(oper("<", resdom->restype, resdom->restype, FALSE)) != sortcl->opoid) - { - /* ---------- - * Sort order isn't in ascending order. - * ---------- - */ - if (ScanDirectionIsForward(dir)) - return nodir; - dir = BackwardScanDirection; - } - else - { - /* ---------- - * Sort order is in ascending order. - * ---------- - */ - if (ScanDirectionIsBackward(dir)) - return nodir; - dir = ForwardScanDirection; - } - - key_no++; - } - - /* ---------- - * Index matches ORDER BY - sort not required - * ---------- - */ - return dir; -} diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c index 6471ad4e41..1492df8b03 100644 --- a/src/backend/optimizer/plan/setrefs.c +++ b/src/backend/optimizer/plan/setrefs.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/setrefs.c,v 1.55 1999/08/18 04:15:16 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/setrefs.c,v 1.56 1999/08/21 03:49:03 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -52,7 +52,6 @@ static void replace_vars_with_subplan_refs(Node *clause, List *subplanTargetList); static bool replace_vars_with_subplan_refs_walker(Node *node, replace_vars_with_subplan_refs_context *context); -static List *pull_agg_clause(Node *clause); static bool pull_agg_clause_walker(Node *node, List **listptr); static bool check_having_for_ungrouped_vars_walker(Node *node, check_having_for_ungrouped_vars_context *context); @@ -416,23 +415,9 @@ replace_vars_with_subplan_refs_walker(Node *node, return false; if (IsA(node, Var)) { - /* - * It could be that this varnode has been created by make_groupplan - * and is already set up to reference the subplan target list. We - * recognize that case by varno = 1, varnoold = -1, varattno = - * varoattno, and varlevelsup = 0. (Probably ought to have an - * explicit flag, but this should do for now.) - */ Var *var = (Var *) node; TargetEntry *subplanVar; - if (var->varno == (Index) 1 && - var->varnoold == ((Index) -1) && - var->varattno == var->varoattno && - var->varlevelsup == 0) - return false; /* OK to leave it alone */ - - /* Otherwise it had better be in the subplan list. */ subplanVar = match_varid(var, context->subplanTargetList); if (!subplanVar) elog(ERROR, "replace_vars_with_subplan_refs: variable not in target list"); @@ -461,7 +446,6 @@ replace_vars_with_subplan_refs_walker(Node *node, * the tuples returned by its left tree subplan. * * If there is a qual list (from a HAVING clause), similarly update * vars in it to point to the subplan target list. - * * Generate the aggNode->aggs list of Aggref nodes contained in the Agg. * * The return value is TRUE if all qual clauses include Aggrefs, or FALSE * if any do not (caller may choose to raise an error condition). @@ -475,7 +459,6 @@ set_agg_tlist_references(Agg *aggNode) bool all_quals_ok; subplanTargetList = aggNode->plan.lefttree->targetlist; - aggNode->aggs = NIL; foreach(tl, aggNode->plan.targetlist) { @@ -484,24 +467,19 @@ set_agg_tlist_references(Agg *aggNode) replace_vars_with_subplan_refs(tle->expr, (Index) 0, subplanTargetList); - aggNode->aggs = nconc(pull_agg_clause(tle->expr), aggNode->aggs); } all_quals_ok = true; foreach(ql, aggNode->plan.qual) { Node *qual = lfirst(ql); - List *qualaggs; replace_vars_with_subplan_refs(qual, (Index) 0, subplanTargetList); - qualaggs = pull_agg_clause(qual); - if (qualaggs == NIL) + if (pull_agg_clause(qual) == NIL) all_quals_ok = false; /* this qual clause has no agg * functions! */ - else - aggNode->aggs = nconc(qualaggs, aggNode->aggs); } return all_quals_ok; @@ -514,7 +492,7 @@ set_agg_tlist_references(Agg *aggNode) * Returns list of Aggref nodes found. Note the nodes themselves are not * copied, only referenced. */ -static List * +List * pull_agg_clause(Node *clause) { List *result = NIL; @@ -545,10 +523,6 @@ pull_agg_clause_walker(Node *node, List **listptr) * exprIsAggOrGroupCol()). But that routine currently does not check subplans, * because the necessary info is not computed until the planner runs. * This ought to be cleaned up someday. - * - * NOTE: the havingClause has been cnf-ified, so AND subclauses have been - * turned into a plain List. Thus, this routine has to cope with List nodes - * where the routine above does not... */ void @@ -588,10 +562,13 @@ check_having_for_ungrouped_vars_walker(Node *node, foreach(gl, context->groupClause) { - Var *groupexpr = get_groupclause_expr(lfirst(gl), - context->targetList); + GroupClause *gcl = lfirst(gl); + Node *groupexpr; - if (var_equal((Var *) thisarg, groupexpr)) + groupexpr = get_sortgroupclause_expr(gcl, + context->targetList); + /* XXX is var_equal correct, or should we use equal()? */ + if (var_equal((Var *) thisarg, (Var *) groupexpr)) { contained_in_group_clause = true; break; diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c index d0022b4cbc..188379c9a2 100644 --- a/src/backend/optimizer/plan/subselect.c +++ b/src/backend/optimizer/plan/subselect.c @@ -6,7 +6,7 @@ * Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/subselect.c,v 1.21 1999/07/16 04:59:20 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/subselect.c,v 1.22 1999/08/21 03:49:03 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -552,9 +552,7 @@ SS_finalize_plan(Plan *plan) break; case T_Agg: - finalize_primnode_walker((Node *) ((Agg *) plan)->aggs, - &results); - Assert(results.subplans == NIL); + /* XXX Code used to reject subplans in Aggref args; needed?? */ break; case T_SeqScan: diff --git a/src/backend/optimizer/prep/preptlist.c b/src/backend/optimizer/prep/preptlist.c index 0cc4780807..2a9ddfc716 100644 --- a/src/backend/optimizer/prep/preptlist.c +++ b/src/backend/optimizer/prep/preptlist.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/prep/preptlist.c,v 1.29 1999/08/09 03:13:31 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/prep/preptlist.c,v 1.30 1999/08/21 03:49:05 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -163,7 +163,6 @@ replace_matching_resname(List *new_tlist, List *old_tlist) { TargetEntry *old_tle = (TargetEntry *) lfirst(temp); - old_tle = lfirst(temp); if (!strcmp(old_tle->resdom->resname, new_tle->resdom->resname)) { @@ -174,6 +173,7 @@ replace_matching_resname(List *new_tlist, List *old_tlist) if (matching_old_tl) { + /* XXX safe to modify old resdom? */ matching_old_tl->resdom->resno = new_tle->resdom->resno; t_list = lappend(t_list, matching_old_tl); } @@ -197,45 +197,42 @@ replace_matching_resname(List *new_tlist, List *old_tlist) { TargetEntry *old_tle, *new_tl; - Resdom *newresno; old_tle = lfirst(temp); if (old_tle->resdom->resno < 0) { - newresno = (Resdom *) copyObject((Node *) old_tle->resdom); - newresno->resno = length(t_list) + 1; - newresno->resjunk = true; - new_tl = makeTargetEntry(newresno, old_tle->expr); + Resdom *newresdom; + + newresdom = (Resdom *) copyObject((Node *) old_tle->resdom); + newresdom->resno = length(t_list) + 1; + newresdom->resjunk = true; + new_tl = makeTargetEntry(newresdom, old_tle->expr); t_list = lappend(t_list, new_tl); } - /* * Also it is possible that the parser or rewriter added some junk - * attributes to hold GROUP BY expressions which are not part of + * attributes to hold ORDER/GROUP BY expressions which are not part of * the result attributes. We can simply identify them by looking - * at the resgroupref in the TLE's resdom, which is a unique - * number telling which TLE belongs to which GroupClause. + * at the ressortgroupref in the TLE's resdom, which is a unique + * number telling which TLE belongs to which Sort/GroupClause. */ - if (old_tle->resdom->resgroupref > 0) + else if (old_tle->resdom->ressortgroupref > 0) { - bool already_there = FALSE; - TargetEntry *new_tle; - Resdom *newresno; + bool already_there = false; /* * Check if the tle is already in the new list */ foreach(i, t_list) { - new_tle = (TargetEntry *) lfirst(i); + TargetEntry *new_tle = (TargetEntry *) lfirst(i); - if (new_tle->resdom->resgroupref == - old_tle->resdom->resgroupref) + if (new_tle->resdom->ressortgroupref == + old_tle->resdom->ressortgroupref) { - already_there = TRUE; + already_there = true; break; } - } /* @@ -243,10 +240,12 @@ replace_matching_resname(List *new_tlist, List *old_tlist) */ if (!already_there) { - newresno = (Resdom *) copyObject((Node *) old_tle->resdom); - newresno->resno = length(t_list) + 1; - newresno->resjunk = true; - new_tl = makeTargetEntry(newresno, old_tle->expr); + Resdom *newresdom; + + newresdom = (Resdom *) copyObject((Node *) old_tle->resdom); + newresdom->resno = length(t_list) + 1; + newresdom->resjunk = true; + new_tl = makeTargetEntry(newresdom, old_tle->expr); t_list = lappend(t_list, new_tl); } } diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c index 21afad92b8..54e84739d2 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.38 1999/08/16 02:17:55 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepunion.c,v 1.39 1999/08/21 03:49:05 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -159,7 +159,7 @@ plan_union_queries(Query *parse) union_plans = lcons(union_planner(parse), NIL); union_rts = lcons(parse->rtable, NIL); - /* Append the remainging UNION ALLs */ + /* Append the remaining UNION ALLs */ foreach(ulist, union_all_queries) { Query *union_all_query = lfirst(ulist); @@ -172,14 +172,19 @@ plan_union_queries(Query *parse) /* We have already split UNION and UNION ALL and we made it consistent */ if (!last_union_all_flag) { + /* Need SELECT DISTINCT behavior to implement UNION. + * Set uniqueFlag properly, put back the held sortClause, + * and add any missing columns to the sort clause. + */ parse->uniqueFlag = "*"; - parse->sortClause = transformSortClause(NULL, NIL, - hold_sortClause, - parse->targetList, "*"); + parse->sortClause = addAllTargetsToSortList(hold_sortClause, + parse->targetList); } else + { /* needed so we don't take the flag from the first query */ parse->uniqueFlag = NULL; + } /* Make sure we don't try to apply the first query's grouping stuff * to the Append node, either. Basically we don't want union_planner diff --git a/src/backend/optimizer/util/tlist.c b/src/backend/optimizer/util/tlist.c index 921bb1d671..37a790cc3d 100644 --- a/src/backend/optimizer/util/tlist.c +++ b/src/backend/optimizer/util/tlist.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/util/tlist.c,v 1.38 1999/08/10 03:00:15 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/util/tlist.c,v 1.39 1999/08/21 03:49:07 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -19,7 +19,7 @@ #include "optimizer/tlist.h" #include "optimizer/var.h" -static Node *flatten_tlist_vars_mutator(Node *node, List *flat_tlist); +static Node *unflatten_tlist_mutator(Node *node, List *flat_tlist); /***************************************************************************** * ---------- RELATION node target list routines ---------- @@ -89,7 +89,7 @@ tlist_member(Var *var, List *tlist) void add_var_to_tlist(RelOptInfo *rel, Var *var) { - if (tlistentry_member(var, rel->targetlist) == NULL) + if (! tlistentry_member(var, rel->targetlist)) { /* XXX is copyObject necessary here? */ rel->targetlist = lappend(rel->targetlist, @@ -156,27 +156,6 @@ get_actual_tlist(List *tlist) * ---------- GENERAL target list routines ---------- *****************************************************************************/ -/* - * Routine to get the resdom out of a targetlist. - */ -Resdom * -tlist_resdom(List *tlist, Resdom *resnode) -{ - List *i; - - foreach(i, tlist) - { - TargetEntry *tle = (TargetEntry *) lfirst(i); - Resdom *resdom = tle->resdom; - - /* Since resnos are supposed to be unique */ - if (resnode->resno == resdom->resno) - return resdom; - } - return (Resdom *) NULL; -} - - /* * match_varid * Searches a target list for an entry matching a given var. @@ -276,49 +255,68 @@ copy_vars(List *target, List *source) * flatten_tlist * Create a target list that only contains unique variables. * - * * 'tlist' is the current target list * * Returns the "flattened" new target list. * + * The result is entirely new structure sharing no nodes with the original. + * Copying the Var nodes is probably overkill, but be safe for now. */ List * flatten_tlist(List *tlist) { List *vlist = pull_var_clause((Node *) tlist); - int last_resdomno = 1; - List *new_tlist = NIL; + List *new_tlist; + + new_tlist = add_to_flat_tlist(NIL, vlist); + freeList(vlist); + return new_tlist; +} + +/* + * add_to_flat_tlist + * Add more vars to a flattened tlist (if they're not already in it) + * + * 'tlist' is the flattened tlist + * 'vars' is a list of var nodes + * + * Returns the extended tlist. + */ +List * +add_to_flat_tlist(List *tlist, List *vars) +{ + int next_resdomno = length(tlist) + 1; List *v; - foreach(v, vlist) + foreach(v, vars) { Var *var = lfirst(v); - if (! tlistentry_member(var, new_tlist)) + if (! tlistentry_member(var, tlist)) { Resdom *r; - r = makeResdom(last_resdomno++, + r = makeResdom(next_resdomno++, var->vartype, var->vartypmod, NULL, (Index) 0, (Oid) 0, false); - new_tlist = lappend(new_tlist, - makeTargetEntry(r, (Node *) var)); + tlist = lappend(tlist, + makeTargetEntry(r, copyObject(var))); } } - freeList(vlist); - - return new_tlist; + return tlist; } /* - * flatten_tlist_vars - * Redoes the target list of a query by replacing vars within + * unflatten_tlist + * Reconstructs the target list of a query by replacing vars within * target expressions with vars from the 'flattened' target list. * + * XXX is this really necessary? Why can't we just use the tlist as is? + * * 'full_tlist' is the original target list * 'flat_tlist' is the flattened (var-only) target list * @@ -326,21 +324,21 @@ flatten_tlist(List *tlist) * */ List * -flatten_tlist_vars(List *full_tlist, List *flat_tlist) +unflatten_tlist(List *full_tlist, List *flat_tlist) { - return (List *) flatten_tlist_vars_mutator((Node *) full_tlist, - flat_tlist); + return (List *) unflatten_tlist_mutator((Node *) full_tlist, + flat_tlist); } static Node * -flatten_tlist_vars_mutator(Node *node, List *flat_tlist) +unflatten_tlist_mutator(Node *node, List *flat_tlist) { if (node == NULL) return NULL; if (IsA(node, Var)) return (Node *) get_expr(match_varid((Var *) node, flat_tlist)); - return expression_tree_mutator(node, flatten_tlist_vars_mutator, + return expression_tree_mutator(node, unflatten_tlist_mutator, (void *) flat_tlist); } @@ -354,20 +352,28 @@ get_expr(TargetEntry *tle) return (Var *) tle->expr; } - -Var * -get_groupclause_expr(GroupClause *groupClause, List *targetList) +/* + * get_sortgroupclause_expr + * Find the targetlist entry matching the given SortClause + * (or GroupClause) by ressortgroupref, and return its expression. + * + * Because GroupClause is typedef'd as SortClause, either kind of + * node can be passed without casting. + */ +Node * +get_sortgroupclause_expr(SortClause *sortClause, List *targetList) { + Index refnumber = sortClause->tleSortGroupRef; List *l; foreach(l, targetList) { TargetEntry *tle = (TargetEntry *) lfirst(l); - if (tle->resdom->resgroupref == groupClause->tleGroupref) - return get_expr(tle); + + if (tle->resdom->ressortgroupref == refnumber) + return tle->expr; } - elog(ERROR, - "get_groupclause_expr: GROUP BY expression not found in targetlist"); - return NULL; + elog(ERROR, "get_sortgroupclause_expr: ORDER/GROUP BY expression not found in targetlist"); + return NULL; /* keep compiler quiet */ } diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c index e3c9feb677..2c4d1c7a7e 100644 --- a/src/backend/parser/analyze.c +++ b/src/backend/parser/analyze.c @@ -5,7 +5,7 @@ * * Copyright (c) 1994, Regents of the University of California * - * $Id: analyze.c,v 1.117 1999/08/15 06:46:49 thomas Exp $ + * $Id: analyze.c,v 1.118 1999/08/21 03:48:55 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -292,7 +292,6 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) * transformSortClause because it also handles uniqueFlag. */ qry->sortClause = transformSortClause(pstate, - NIL, NIL, qry->targetList, qry->uniqueFlag); @@ -1039,7 +1038,6 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt) qry->sortClause = transformSortClause(pstate, stmt->sortClause, - NIL, qry->targetList, qry->uniqueFlag); diff --git a/src/backend/parser/parse_agg.c b/src/backend/parser/parse_agg.c index 3cca6d681f..a500782297 100644 --- a/src/backend/parser/parse_agg.c +++ b/src/backend/parser/parse_agg.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/parse_agg.c,v 1.27 1999/08/16 02:10:13 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/parse_agg.c,v 1.28 1999/08/21 03:48:55 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -154,7 +154,7 @@ parseCheckAggregates(ParseState *pstate, Query *qry) GroupClause *grpcl = lfirst(tl); Node *expr; - expr = (Node *) get_groupclause_expr(grpcl, qry->targetList); + expr = get_sortgroupclause_expr(grpcl, qry->targetList); if (contain_agg_clause(expr)) elog(ERROR, "Aggregates not allowed in GROUP BY clause"); groupClauses = lcons(expr, groupClauses); @@ -293,10 +293,8 @@ ParseAgg(ParseState *pstate, char *aggname, Oid basetype, aggref->aggname = pstrdup(aggname); aggref->basetype = aggform->aggbasetype; aggref->aggtype = fintype; - aggref->target = lfirst(target); - if (usenulls) - aggref->usenulls = true; + aggref->usenulls = usenulls; pstate->p_hasAggs = true; diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c index 275d131bab..234987fb5f 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.43 1999/08/16 02:10:13 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.44 1999/08/21 03:48:55 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -34,6 +34,9 @@ static TargetEntry *findTargetlistEntry(ParseState *pstate, Node *node, 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, + List *targetlist, char *opname); +static bool exprIsInSortList(Node *expr, List *sortList, List *targetList); #ifdef ENABLE_OUTER_JOINS static Node *transformUsingClause(ParseState *pstate, List *onList, @@ -464,39 +467,25 @@ List * transformGroupClause(ParseState *pstate, List *grouplist, List *targetlist) { List *glist = NIL, - *gl, - *othergl; - int nextgroupref = 1; + *gl; foreach(gl, grouplist) { - TargetEntry *restarget; - Resdom *resdom; + TargetEntry *tle; - restarget = findTargetlistEntry(pstate, lfirst(gl), - targetlist, GROUP_CLAUSE); - resdom = restarget->resdom; + tle = findTargetlistEntry(pstate, lfirst(gl), + targetlist, GROUP_CLAUSE); /* avoid making duplicate grouplist entries */ - foreach(othergl, glist) - { - GroupClause *gcl = (GroupClause *) lfirst(othergl); - - if (equal(get_groupclause_expr(gcl, targetlist), - restarget->expr)) - break; - } - - if (othergl == NIL) /* not in grouplist already */ + if (! exprIsInSortList(tle->expr, glist, targetlist)) { GroupClause *grpcl = makeNode(GroupClause); - grpcl->tleGroupref = nextgroupref++; - resdom->resgroupref = grpcl->tleGroupref; + grpcl->tleSortGroupRef = assignSortGroupRef(tle, targetlist); - grpcl->grpOpoid = oprid(oper("<", - resdom->restype, - resdom->restype, false)); + grpcl->sortop = oprid(oper("<", + tle->resdom->restype, + tle->resdom->restype, false)); glist = lappend(glist, grpcl); } @@ -513,141 +502,169 @@ transformGroupClause(ParseState *pstate, List *grouplist, List *targetlist) List * transformSortClause(ParseState *pstate, List *orderlist, - List *sortlist, List *targetlist, char *uniqueFlag) { - List *s = NIL; + List *sortlist = NIL; + List *olitem; - while (orderlist != NIL) + /* Transform all the explicit ORDER BY clauses */ + + foreach(olitem, orderlist) { - SortGroupBy *sortby = lfirst(orderlist); - SortClause *sortcl = makeNode(SortClause); - TargetEntry *restarget; - Resdom *resdom; + SortGroupBy *sortby = lfirst(olitem); + TargetEntry *tle; - restarget = findTargetlistEntry(pstate, sortby->node, - targetlist, ORDER_CLAUSE); + tle = findTargetlistEntry(pstate, sortby->node, + targetlist, ORDER_CLAUSE); - sortcl->resdom = resdom = restarget->resdom; - - /* - * if we have InvalidOid, then this is a NULL field and don't need - * to sort - */ - if (resdom->restype == InvalidOid) - resdom->restype = INT4OID; - - sortcl->opoid = oprid(oper(sortby->useOp, - resdom->restype, - resdom->restype, false)); - if (sortlist == NIL) - s = sortlist = lcons(sortcl, NIL); - else - { - List *i; - - foreach(i, sortlist) - { - SortClause *scl = (SortClause *) lfirst(i); - - if (scl->resdom == sortcl->resdom) - break; - } - if (i == NIL) /* not in sortlist already */ - { - lnext(s) = lcons(sortcl, NIL); - s = lnext(s); - } - else - pfree(sortcl); /* get rid of this */ - } - orderlist = lnext(orderlist); + 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. + */ + if (uniqueFlag) { - List *i; - if (uniqueFlag[0] == '*') { - /* * concatenate all elements from target list that are not * already in the sortby list */ - foreach(i, targetlist) - { - TargetEntry *tlelt = (TargetEntry *) lfirst(i); - - s = sortlist; - while (s != NIL) - { - SortClause *sortcl = lfirst(s); - - /* - * We use equal() here because we are called for UNION - * from the optimizer, and at that point, the sort - * clause resdom pointers don't match the target list - * resdom pointers - */ - if (equal(sortcl->resdom, tlelt->resdom)) - break; - s = lnext(s); - } - if (s == NIL) - { - /* not a member of the sortclauses yet */ - SortClause *sortcl = makeNode(SortClause); - - if (tlelt->resdom->restype == InvalidOid) - tlelt->resdom->restype = INT4OID; - - sortcl->resdom = tlelt->resdom; - sortcl->opoid = any_ordering_op(tlelt->resdom->restype); - - sortlist = lappend(sortlist, sortcl); - } - } + sortlist = addAllTargetsToSortList(sortlist, targetlist); } else { - TargetEntry *tlelt = NULL; + TargetEntry *tle = NULL; char *uniqueAttrName = uniqueFlag; + List *i; /* only create sort clause with the specified unique attribute */ foreach(i, targetlist) { - tlelt = (TargetEntry *) lfirst(i); - if (strcmp(tlelt->resdom->resname, uniqueAttrName) == 0) + tle = (TargetEntry *) lfirst(i); + if (strcmp(tle->resdom->resname, uniqueAttrName) == 0) break; } if (i == NIL) elog(ERROR, "All fields in the UNIQUE ON clause must appear in the target list"); - foreach(s, sortlist) - { - SortClause *sortcl = lfirst(s); - - if (sortcl->resdom == tlelt->resdom) - break; - } - if (s == NIL) - { - /* not a member of the sortclauses yet */ - SortClause *sortcl = makeNode(SortClause); - - sortcl->resdom = tlelt->resdom; - sortcl->opoid = any_ordering_op(tlelt->resdom->restype); - - sortlist = lappend(sortlist, sortcl); - } + sortlist = addTargetToSortList(tle, sortlist, targetlist, NULL); } } return sortlist; } +/* + * 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. + * This is typically used to help implement SELECT DISTINCT. + * + * Returns the updated ORDER BY list. + */ +List * +addAllTargetsToSortList(List *sortlist, List *targetlist) +{ + List *i; + + foreach(i, targetlist) + { + TargetEntry *tle = (TargetEntry *) lfirst(i); + + sortlist = addTargetToSortList(tle, sortlist, targetlist, NULL); + } + return sortlist; +} + +/* + * addTargetToSortList + * If the given targetlist entry isn't already in the ORDER BY list, + * add it to the end of the list, using the sortop with given name + * or any available sort operator if opname == NULL. + * + * Returns the updated ORDER BY list. + */ +static List * +addTargetToSortList(TargetEntry *tle, List *sortlist, List *targetlist, + char *opname) +{ + /* avoid making duplicate sortlist entries */ + if (! exprIsInSortList(tle->expr, sortlist, targetlist)) + { + SortClause *sortcl = makeNode(SortClause); + + sortcl->tleSortGroupRef = assignSortGroupRef(tle, targetlist); + + if (opname) + sortcl->sortop = oprid(oper(opname, + tle->resdom->restype, + tle->resdom->restype, false)); + else + sortcl->sortop = any_ordering_op(tle->resdom->restype); + + sortlist = lappend(sortlist, sortcl); + } + return sortlist; +} + +/* + * assignSortGroupRef + * Assign the targetentry an unused ressortgroupref, if it doesn't + * already have one. Return the assigned or pre-existing refnumber. + * + * 'tlist' is the targetlist containing (or to contain) the given targetentry. + */ +Index +assignSortGroupRef(TargetEntry *tle, List *tlist) +{ + Index maxRef; + List *l; + + if (tle->resdom->ressortgroupref) /* already has one? */ + return tle->resdom->ressortgroupref; + + /* easiest way to pick an unused refnumber: max used + 1 */ + maxRef = 0; + foreach(l, tlist) + { + Index ref = ((TargetEntry *) lfirst(l))->resdom->ressortgroupref; + + if (ref > maxRef) + maxRef = ref; + } + tle->resdom->ressortgroupref = maxRef + 1; + return tle->resdom->ressortgroupref; +} + +/* + * exprIsInSortList + * Is the given expression already in the sortlist? + * Note we will say 'yes' if it is equal() to any sortlist item, + * even though that might be a different targetlist member. + * + * Works for both SortClause and GroupClause lists. + */ +static bool +exprIsInSortList(Node *expr, List *sortList, List *targetList) +{ + List *i; + + foreach(i, sortList) + { + SortClause *scl = (SortClause *) lfirst(i); + + if (equal(expr, get_sortgroupclause_expr(scl, targetList))) + return true; + } + return false; +} + /* transformUnionClause() * Transform a UNION clause. * Note that the union clause is actually a fully-formed select structure. diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c index 344c87920b..4a227de2c5 100644 --- a/src/backend/parser/parse_func.c +++ b/src/backend/parser/parse_func.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.52 1999/08/16 02:08:59 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.53 1999/08/21 03:48:55 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -63,7 +63,6 @@ static int match_argtypes(int nargs, CandidateList function_typeids, CandidateList *candidates); static List *setup_tlist(char *attname, Oid relid); -static List *setup_base_tlist(Oid typeid); static Oid *func_select_candidate(int nargs, Oid *input_typeids, CandidateList candidates); static int agg_get_candidates(char *aggname, Oid typeId, CandidateList *candidates); @@ -1312,7 +1311,7 @@ setup_tlist(char *attname, Oid relid) ** Build a tlist that extracts a base type from the tuple ** returned by the executor. */ -static List * +List * setup_base_tlist(Oid typeid) { TargetEntry *tle; diff --git a/src/backend/rewrite/rewriteManip.c b/src/backend/rewrite/rewriteManip.c index 38464d3438..f88e936412 100644 --- a/src/backend/rewrite/rewriteManip.c +++ b/src/backend/rewrite/rewriteManip.c @@ -6,7 +6,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteManip.c,v 1.38 1999/07/17 20:17:38 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteManip.c,v 1.39 1999/08/21 03:49:13 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -14,6 +14,7 @@ #include "optimizer/clauses.h" #include "parser/parsetree.h" +#include "parser/parse_clause.h" #include "rewrite/rewriteManip.h" #include "utils/builtins.h" #include "utils/lsyscache.h" @@ -533,25 +534,32 @@ AddNotQual(Query *parsetree, Node *qual) } +/* + * Add all expressions used by the given GroupClause list to the + * parsetree's targetlist and groupclause list. + * + * tlist is the old targetlist associated with the input groupclauses. + * + * XXX shouldn't we be checking to see if there are already matching + * entries in parsetree->targetlist? + */ void AddGroupClause(Query *parsetree, List *group_by, List *tlist) { List *l; - List *tl; - GroupClause *groupclause; - TargetEntry *tle; - int new_resno; - - new_resno = length(parsetree->targetList); foreach(l, group_by) { - groupclause = (GroupClause *) copyObject(lfirst(l)); - tle = NULL; + GroupClause *groupclause = (GroupClause *) copyObject(lfirst(l)); + Index refnumber = groupclause->tleSortGroupRef; + TargetEntry *tle = NULL; + List *tl; + + /* Find and copy the groupclause's TLE in the old tlist */ foreach(tl, tlist) { - if (((TargetEntry *) lfirst(tl))->resdom->resgroupref == - groupclause->tleGroupref) + if (((TargetEntry *) lfirst(tl))->resdom->ressortgroupref == + refnumber) { tle = (TargetEntry *) copyObject(lfirst(tl)); break; @@ -560,10 +568,16 @@ AddGroupClause(Query *parsetree, List *group_by, List *tlist) if (tle == NULL) elog(ERROR, "AddGroupClause(): GROUP BY entry not found in rules targetlist"); - tle->resdom->resno = ++new_resno; + /* The ressortgroupref number in the old tlist might be already + * taken in the new tlist, so force assignment of a new number. + */ + tle->resdom->ressortgroupref = 0; + groupclause->tleSortGroupRef = + assignSortGroupRef(tle, parsetree->targetList); + + /* Also need to set the resno and mark it resjunk. */ + tle->resdom->resno = length(parsetree->targetList) + 1; tle->resdom->resjunk = true; - tle->resdom->resgroupref = length(parsetree->groupClause) + 1; - groupclause->tleGroupref = tle->resdom->resgroupref; parsetree->targetList = lappend(parsetree->targetList, tle); parsetree->groupClause = lappend(parsetree->groupClause, groupclause); diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 8f365f2efb..008402e26f 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -3,7 +3,7 @@ * out of it's tuple * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.21 1999/07/17 20:17:59 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.22 1999/08/21 03:48:53 tgl Exp $ * * This software is copyrighted by Jan Wieck - Hamburg. * @@ -41,6 +41,7 @@ #include "postgres.h" #include "executor/spi.h" #include "optimizer/clauses.h" +#include "optimizer/tlist.h" #include "utils/lsyscache.h" #include "catalog/pg_shadow.h" #include "catalog/pg_index.h" @@ -1248,23 +1249,11 @@ get_rule_expr(QryHier *qh, int rt_index, Node *node, bool varprefix) case T_GroupClause: { GroupClause *grp = (GroupClause *) node; - List *l; - TargetEntry *tle = NULL; + Node *groupexpr; - foreach(l, qh->query->targetList) - { - if (((TargetEntry *) lfirst(l))->resdom->resgroupref == - grp->tleGroupref) - { - tle = (TargetEntry *) lfirst(l); - break; - } - } - - if (tle == NULL) - elog(ERROR, "GROUP BY expression not found in targetlist"); - - return get_rule_expr(qh, rt_index, (Node *) tle, varprefix); + groupexpr = get_sortgroupclause_expr(grp, + qh->query->targetList); + return get_rule_expr(qh, rt_index, groupexpr, varprefix); } break; diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index 3b9cf8a8c9..132f052667 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -6,7 +6,7 @@ * * Copyright (c) 1994, Regents of the University of California * - * $Id: execnodes.h,v 1.33 1999/07/16 17:07:33 momjian Exp $ + * $Id: execnodes.h,v 1.34 1999/08/21 03:49:08 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -572,6 +572,7 @@ typedef struct MaterialState typedef struct AggState { CommonScanState csstate; /* its first field is NodeTag */ + List *aggs; /* all Aggref nodes in targetlist & quals */ bool agg_done; } AggState; diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index ab22b737e4..5a1d07ec39 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.77 1999/07/18 03:45:01 tgl Exp $ + * $Id: parsenodes.h,v 1.78 1999/08/21 03:49:09 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -48,28 +48,29 @@ typedef struct Query bool hasAggs; /* has aggregates in target list */ bool hasSubLinks; /* has subquery SubLink */ + List *rtable; /* list of range table entries */ + List *targetList; /* target list (of TargetEntry) */ + Node *qual; /* qualifications applied to tuples */ + List *rowMark; /* list of RowMark entries */ + char *uniqueFlag; /* NULL, '*', or Unique attribute name */ List *sortClause; /* a list of SortClause's */ - List *rtable; /* list of range table entries */ - List *targetList; /* target list (of TargetEntry) */ - Node *qual; /* qualifications */ - List *rowMark; /* list of RowMark entries */ + List *groupClause; /* a list of GroupClause's */ - List *groupClause; /* list of columns to specified in GROUP - * BY */ - Node *havingQual; /* qualification of each group */ + Node *havingQual; /* qualifications applied to groups */ List *intersectClause; - List *unionClause; /* unions are linked under the previous * query */ + Node *limitOffset; /* # of result tuples to skip */ Node *limitCount; /* # of result tuples to return */ /* internal to planner */ - List *base_rel_list; /* base relation list */ - List *join_rel_list; /* list of relation involved in joins */ + List *base_rel_list; /* list of base-relation RelOptInfos */ + List *join_rel_list; /* list of join-relation RelOptInfos */ + List *query_pathkeys; /* pathkeys for query_planner()'s result */ } Query; @@ -608,7 +609,7 @@ typedef struct InsertStmt List *targetList; /* the target list (of ResTarget) */ List *fromClause; /* the from clause */ Node *whereClause; /* qualifications */ - List *groupClause; /* group by clause */ + List *groupClause; /* GROUP BY clauses */ Node *havingClause; /* having conditional-expression */ List *unionClause; /* union subselect parameters */ bool unionall; /* union without unique sort */ @@ -652,7 +653,7 @@ typedef struct SelectStmt List *targetList; /* the target list (of ResTarget) */ List *fromClause; /* the from clause */ Node *whereClause; /* qualifications */ - List *groupClause; /* group by clause */ + List *groupClause; /* GROUP BY clauses */ Node *havingClause; /* having conditional-expression */ List *intersectClause; List *exceptClause; @@ -950,25 +951,28 @@ typedef struct RangeTblEntry /* * SortClause - - * used in the sort clause for retrieves and cursors + * representation of ORDER BY clauses + * + * tleSortGroupRef must match ressortgroupref of exactly one Resdom of the + * associated targetlist; that is the expression to be sorted (or grouped) by. + * sortop is the OID of the ordering operator. */ typedef struct SortClause { NodeTag type; - Resdom *resdom; /* attributes in tlist to be sorted */ - Oid opoid; /* sort operators */ + Index tleSortGroupRef; /* reference into targetlist */ + Oid sortop; /* the sort operator to use */ } SortClause; /* * GroupClause - - * used in the GROUP BY clause + * representation of GROUP BY clauses + * + * GroupClause is exactly like SortClause except for the nodetag value + * (and it's probably not even really necessary to have two different + * nodetags...). We have routines that operate interchangeably on both. */ -typedef struct GroupClause -{ - NodeTag type; - Oid grpOpoid; /* the sort operator to use */ - Index tleGroupref; /* reference into targetlist */ -} GroupClause; +typedef SortClause GroupClause; #define ROW_MARK_FOR_UPDATE (1 << 0) #define ROW_ACL_FOR_UPDATE (1 << 1) diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h index 532be5196b..095ee074d3 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.29 1999/08/09 06:20:27 momjian Exp $ + * $Id: plannodes.h,v 1.30 1999/08/21 03:49:09 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -232,7 +232,6 @@ typedef struct HashJoin typedef struct Agg { Plan plan; - List *aggs; AggState *aggstate; } Agg; diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h index 2c4d0ffaa7..4eea81446b 100644 --- a/src/include/nodes/primnodes.h +++ b/src/include/nodes/primnodes.h @@ -6,7 +6,7 @@ * * Copyright (c) 1994, Regents of the University of California * - * $Id: primnodes.h,v 1.33 1999/08/16 02:17:39 tgl Exp $ + * $Id: primnodes.h,v 1.34 1999/08/21 03:49:09 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -25,15 +25,33 @@ /* ---------------- * Resdom (Result Domain) * resno - attribute number - * restype - type of the resdom - * restypmod - type-specific modifier of the result + * restype - type of the value + * restypmod - type-specific modifier of the value * resname - name of the resdom (could be NULL) + * ressortgroupref - nonzero if referenced by a sort/group clause * reskey - order of key in a sort (for those > 0) - * reskeyop - sort operator Oid - * resgroupref - set to nonzero if referenced from a group by clause + * reskeyop - sort operator's regproc Oid * resjunk - set to true to eliminate the attribute * from final target list * + * Notes: + * ressortgroupref is the parse/plan-time representation of ORDER BY and + * GROUP BY items. Targetlist entries with ressortgroupref=0 are not + * sort/group items. If ressortgroupref>0, then this item is an ORDER BY or + * GROUP BY value. No two entries in a targetlist may have the same nonzero + * ressortgroupref --- but there is no particular meaning to the nonzero + * values, except as tags. (For example, one must not assume that lower + * ressortgroupref means a more significant sort key.) The order of the + * associated SortClause or GroupClause lists determine the semantics. + * + * reskey and reskeyop are the execution-time representation of sorting. + * reskey must be zero in any non-sort-key item. The reskey of sort key + * targetlist items for a sort plan node is 1,2,...,n for the n sort keys. + * The reskeyop of each such targetlist item is the sort operator's + * regproc OID. reskeyop will be zero in non-sort-key items. + * + * Both reskey and reskeyop are typically zero during parse/plan stages. + * The executor does not pay any attention to ressortgroupref. * ---------------- */ typedef struct Resdom @@ -43,9 +61,9 @@ typedef struct Resdom Oid restype; int32 restypmod; char *resname; + Index ressortgroupref; Index reskey; Oid reskeyop; - Index resgroupref; bool resjunk; } Resdom; @@ -275,7 +293,8 @@ typedef struct Iter * basetype - base type Oid of the aggregate * aggtype - type Oid of final result of the aggregate * target - attribute or expression we are aggregating on - * aggno - index to ecxt_values + * usenulls - TRUE to accept null values as inputs + * aggno - workspace for nodeAgg.c executor * ---------------- */ typedef struct Aggref @@ -285,8 +304,8 @@ typedef struct Aggref Oid basetype; Oid aggtype; Node *target; - int aggno; bool usenulls; + int aggno; } Aggref; /* ---------------- diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h index b0ec64c3e3..58da94368c 100644 --- a/src/include/optimizer/paths.h +++ b/src/include/optimizer/paths.h @@ -7,7 +7,7 @@ * * Copyright (c) 1994, Regents of the University of California * - * $Id: paths.h,v 1.34 1999/08/16 02:17:45 tgl Exp $ + * $Id: paths.h,v 1.35 1999/08/21 03:49:15 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -56,11 +56,15 @@ typedef enum extern PathKeysComparison compare_pathkeys(List *keys1, List *keys2); extern bool pathkeys_contained_in(List *keys1, List *keys2); -extern Path *get_cheapest_path_for_pathkeys(List *paths, List *pathkeys); +extern Path *get_cheapest_path_for_pathkeys(List *paths, List *pathkeys, + bool indexpaths_only); extern List *build_index_pathkeys(Query *root, RelOptInfo *rel, RelOptInfo *index); extern List *build_join_pathkeys(List *outer_pathkeys, List *join_rel_tlist, List *joinclauses); +extern bool commute_pathkeys(List *pathkeys); +extern List *make_pathkeys_for_sortclauses(List *sortclauses, + List *tlist); extern List *find_mergeclauses_for_pathkeys(List *pathkeys, List *restrictinfos); extern List *make_pathkeys_for_mergeclauses(List *mergeclauses, diff --git a/src/include/optimizer/planmain.h b/src/include/optimizer/planmain.h index 0ba017471d..38ff367384 100644 --- a/src/include/optimizer/planmain.h +++ b/src/include/optimizer/planmain.h @@ -6,7 +6,7 @@ * * Copyright (c) 1994, Regents of the University of California * - * $Id: planmain.h,v 1.30 1999/08/09 00:56:04 tgl Exp $ + * $Id: planmain.h,v 1.31 1999/08/21 03:49:15 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -33,7 +33,7 @@ extern Sort *make_sort(List *tlist, Oid nonameid, Plan *lefttree, int keycount); extern Agg *make_agg(List *tlist, Plan *lefttree); extern Group *make_group(List *tlist, bool tuplePerGroup, int ngrp, - AttrNumber *grpColIdx, Sort *lefttree); + AttrNumber *grpColIdx, Plan *lefttree); extern Unique *make_unique(List *tlist, Plan *lefttree, char *uniqueAttr); /* @@ -54,9 +54,14 @@ extern void replace_tlist_with_subplan_refs(List *tlist, Index subvarno, List *subplanTargetList); extern bool set_agg_tlist_references(Agg *aggNode); +extern List *pull_agg_clause(Node *clause); extern void check_having_for_ungrouped_vars(Node *clause, List *groupClause, List *targetList); + +/* + * prep/prepkeyset.c + */ extern void transformKeySetQuery(Query *origNode); #endif /* PLANMAIN_H */ diff --git a/src/include/optimizer/tlist.h b/src/include/optimizer/tlist.h index fb08b70835..f0ddb9ac9d 100644 --- a/src/include/optimizer/tlist.h +++ b/src/include/optimizer/tlist.h @@ -6,7 +6,7 @@ * * Copyright (c) 1994, Regents of the University of California * - * $Id: tlist.h,v 1.20 1999/08/16 02:17:45 tgl Exp $ + * $Id: tlist.h,v 1.21 1999/08/21 03:49:15 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -21,16 +21,17 @@ extern void add_var_to_tlist(RelOptInfo *rel, Var *var); extern TargetEntry *create_tl_element(Var *var, int resdomno); extern List *get_actual_tlist(List *tlist); extern Resdom *tlist_member(Var *var, List *tlist); -extern Resdom *tlist_resdom(List *tlist, Resdom *resnode); extern TargetEntry *match_varid(Var *test_var, List *tlist); extern List *new_unsorted_tlist(List *targetlist); extern List *copy_vars(List *target, List *source); extern List *flatten_tlist(List *tlist); -extern List *flatten_tlist_vars(List *full_tlist, - List *flat_tlist); +extern List *add_to_flat_tlist(List *tlist, List *vars); +extern List *unflatten_tlist(List *full_tlist, + List *flat_tlist); extern Var *get_expr(TargetEntry *tle); -extern Var *get_groupclause_expr(GroupClause *groupClause, List *targetList); +extern Node *get_sortgroupclause_expr(SortClause *sortClause, + List *targetList); #endif /* TLIST_H */ diff --git a/src/include/parser/parse_clause.h b/src/include/parser/parse_clause.h index 3b1aafa07d..6b30301ea0 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.12 1999/07/19 00:26:16 tgl Exp $ + * $Id: parse_clause.h,v 1.13 1999/08/21 03:49:17 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -20,8 +20,11 @@ extern void setTargetTable(ParseState *pstate, char *relname); extern Node *transformWhereClause(ParseState *pstate, Node *where, Node *using); extern List *transformGroupClause(ParseState *pstate, List *grouplist, - List *targetlist); -extern List *transformSortClause(ParseState *pstate, - List *orderlist, List *sortClause, - List *targetlist, char *uniqueFlag); + List *targetlist); +extern List *transformSortClause(ParseState *pstate, List *orderlist, + List *targetlist, char *uniqueFlag); + +extern List *addAllTargetsToSortList(List *sortlist, List *targetlist); +extern Index assignSortGroupRef(TargetEntry *tle, List *tlist); + #endif /* PARSE_CLAUSE_H */ diff --git a/src/include/parser/parse_func.h b/src/include/parser/parse_func.h index 38626290cb..1fdb8f9890 100644 --- a/src/include/parser/parse_func.h +++ b/src/include/parser/parse_func.h @@ -6,7 +6,7 @@ * * Copyright (c) 1994, Regents of the University of California * - * $Id: parse_func.h,v 1.18 1999/07/15 23:04:02 momjian Exp $ + * $Id: parse_func.h,v 1.19 1999/08/21 03:49:17 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -45,6 +45,8 @@ extern Node *ParseNestedFuncOrColumn(ParseState *pstate, Attr *attr, extern Node *ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs, int *curr_resno, int precedence); +extern List *setup_base_tlist(Oid typeid); + extern void func_error(char *caller, char *funcname, int nargs, Oid *argtypes, char *msg);