Commit db436adf authored by Tom Lane's avatar Tom Lane

Major revision of sort-node handling: push knowledge of query

sort order down into planner, instead of handling it only at the very top
level of the planner.  This fixes many things.  An explicit sort is now
avoided if there is a cheaper alternative (typically an indexscan) not
only for ORDER BY, but also for the internal sort of GROUP BY.  It works
even when there is no other reason (such as a WHERE condition) to consider
the indexscan.  It works for indexes on functions.  It works for indexes
on functions, backwards.  It's just so cool...

CAUTION: I have changed the representation of SortClause nodes, therefore
THIS UPDATE BREAKS STORED RULES.  You will need to initdb.
parent 5588c559
......@@ -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
......
......@@ -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);
newnode->hasAggs = from->hasAggs;
newnode->hasSubLinks = from->hasSubLinks;
Node_Copy(from, newnode, rtable);
Node_Copy(from, newnode, targetList);
Node_Copy(from, newnode, qual);
Node_Copy(from, newnode, rowMark);
if (from->uniqueFlag)
newnode->uniqueFlag = pstrdup(from->uniqueFlag);
Node_Copy(from, newnode, sortClause);
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;
foreach(ulist, from->unionClause)
temp_list = lappend(temp_list, copyObject(lfirst(ulist)));
newnode->unionClause = temp_list;
}
/* 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;
}
......
......@@ -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;
}
......
......@@ -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);
}
......
......@@ -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;
}
......
......@@ -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");
}
......
......@@ -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;
......
......@@ -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 ----
****************************************************************************/
......
......@@ -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 &&
......
......@@ -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
****************************************************************************/
......
......@@ -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;
......
......@@ -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
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);
/*
* Determine the cheapest path and create a subplan corresponding to
* it.
* Otherwise, look to see if we have an already-ordered path that is
* cheaper than doing an explicit sort on cheapestpath.
*/
if (final_rel)
return create_plan((Path *) final_rel->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
{
elog(NOTICE, "final relation is null");
return create_plan((Path *) NULL);
/*
* 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);
}
/*****************************************************************************
......
This diff is collapsed.
......@@ -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;
......
......@@ -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:
......
......@@ -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);
}
}
......
......@@ -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
......
......@@ -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 */
}
......@@ -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);
......
......@@ -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;
......
......@@ -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;
while (orderlist != NIL)
{
SortGroupBy *sortby = lfirst(orderlist);
SortClause *sortcl = makeNode(SortClause);
TargetEntry *restarget;
Resdom *resdom;
restarget = findTargetlistEntry(pstate, sortby->node,
targetlist, ORDER_CLAUSE);
List *sortlist = NIL;
List *olitem;
sortcl->resdom = resdom = restarget->resdom;
/* Transform all the explicit ORDER BY clauses */
/*
* 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(olitem, orderlist)
{
SortGroupBy *sortby = lfirst(olitem);
TargetEntry *tle;
foreach(i, sortlist)
{
SortClause *scl = (SortClause *) lfirst(i);
tle = findTargetlistEntry(pstate, sortby->node,
targetlist, ORDER_CLAUSE);
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);
sortlist = addTargetToSortList(tle, sortlist, targetlist, NULL);
}
}
if (sortcl->resdom == tlelt->resdom)
break;
}
if (s == NIL)
{
/* not a member of the sortclauses yet */
SortClause *sortcl = makeNode(SortClause);
return sortlist;
}
sortcl->resdom = tlelt->resdom;
sortcl->opoid = any_ordering_op(tlelt->resdom->restype);
/*
* 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;
sortlist = lappend(sortlist, sortcl);
}
}
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.
......
......@@ -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;
......
......@@ -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);
......
......@@ -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;
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");
Node *groupexpr;
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;
......
......@@ -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;
......
......@@ -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 */
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 */
Node *qual; /* qualifications applied to tuples */
List *rowMark; /* list of RowMark entries */
List *groupClause; /* list of columns to specified in GROUP
* BY */
Node *havingQual; /* qualification of each group */
char *uniqueFlag; /* NULL, '*', or Unique attribute name */
List *sortClause; /* a list of SortClause's */
List *intersectClause;
List *groupClause; /* a list of GroupClause's */
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)
......
......@@ -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;
......
......@@ -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;
/* ----------------
......
......@@ -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,
......
......@@ -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 */
......@@ -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 */
......@@ -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 */
......@@ -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);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment