aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/backend/executor/nodeAgg.c51
-rw-r--r--src/backend/nodes/copyfuncs.c49
-rw-r--r--src/backend/nodes/equalfuncs.c42
-rw-r--r--src/backend/nodes/freefuncs.c17
-rw-r--r--src/backend/nodes/makefuncs.c7
-rw-r--r--src/backend/nodes/outfuncs.c23
-rw-r--r--src/backend/nodes/readfuncs.c40
-rw-r--r--src/backend/optimizer/path/indxpath.c178
-rw-r--r--src/backend/optimizer/path/joinpath.c8
-rw-r--r--src/backend/optimizer/path/pathkeys.c82
-rw-r--r--src/backend/optimizer/plan/createplan.c9
-rw-r--r--src/backend/optimizer/plan/planmain.c102
-rw-r--r--src/backend/optimizer/plan/planner.c657
-rw-r--r--src/backend/optimizer/plan/setrefs.c41
-rw-r--r--src/backend/optimizer/plan/subselect.c6
-rw-r--r--src/backend/optimizer/prep/preptlist.c47
-rw-r--r--src/backend/optimizer/prep/prepunion.c15
-rw-r--r--src/backend/optimizer/util/tlist.c106
-rw-r--r--src/backend/parser/analyze.c4
-rw-r--r--src/backend/parser/parse_agg.c8
-rw-r--r--src/backend/parser/parse_clause.c259
-rw-r--r--src/backend/parser/parse_func.c5
-rw-r--r--src/backend/rewrite/rewriteManip.c42
-rw-r--r--src/backend/utils/adt/ruleutils.c23
-rw-r--r--src/include/nodes/execnodes.h3
-rw-r--r--src/include/nodes/parsenodes.h50
-rw-r--r--src/include/nodes/plannodes.h3
-rw-r--r--src/include/nodes/primnodes.h35
-rw-r--r--src/include/optimizer/paths.h8
-rw-r--r--src/include/optimizer/planmain.h9
-rw-r--r--src/include/optimizer/tlist.h11
-rw-r--r--src/include/parser/parse_clause.h13
-rw-r--r--src/include/parser/parse_func.h4
33 files changed, 961 insertions, 996 deletions
diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c
index fc95d2ce5bc..b600c0e3a8f 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 99b1c39c7e6..4895be72379 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);
+ 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;
}
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 1c53fe6aede..d8538a45066 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 11f92b28d05..cad49be76bd 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 d64fdd8c462..26ebed1d458 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 39b0625d933..226dbb1cef0 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 98385dd96f6..588528daa1d 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 99ce241fed1..6af480629e2 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 55891d87a95..c1ac6a2c4d8 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 44a7b614b69..41a3ff35b48 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 40923fe27da..d1f756fc7c1 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 76e010c9d22..f6f62abfe08 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
+ 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);
}
/*****************************************************************************
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 17ad449839b..b328c40f226 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)
@@ -180,6 +182,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->uniqueFlag)
+ if (parse->sortClause && ! is_sorted)
{
- Plan *sortplan = make_sortplan(tlist, parse->sortClause, result_plan);
-
- return ((Plan *) make_unique(tlist, sortplan, parse->uniqueFlag));
+ result_plan = make_sortplan(tlist, parse->sortClause, result_plan);
}
- else
+
+ /*
+ * Finally, if there is a UNIQUE clause, add the UNIQUE node.
+ */
+ if (parse->uniqueFlag)
{
- 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).
*/
- numCols = length(parse->groupClause);
- if (numCols > 0)
- {
- glc = listCopy(parse->groupClause);
- grpColIdx = (AttrNumber *) palloc(sizeof(AttrNumber) * numCols);
- *groupColIdx = grpColIdx;
- }
-
- sub_tlist = new_unsorted_tlist(tlist); /* make a modifiable copy */
+ sub_tlist = flatten_tlist(tlist);
+ extravars = pull_var_clause(parse->havingQual);
+ sub_tlist = add_to_flat_tlist(sub_tlist, extravars);
+ freeList(extravars);
/*
- * 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.
+ * 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.
*/
- prnt_tlist = tlist; /* scans parent tlist in sync with sl */
- foreach(sl, sub_tlist)
+ numCols = length(parse->groupClause);
+ if (numCols > 0)
{
- TargetEntry *te = (TargetEntry *) lfirst(sl);
- TargetEntry *parentte = (TargetEntry *) lfirst(prnt_tlist);
- Resdom *resdom = te->resdom;
- bool keepInSubPlan = true;
- bool foundGroupClause = false;
int keyno = 0;
+ AttrNumber *grpColIdx;
+ List *gl;
+
+ grpColIdx = (AttrNumber *) palloc(sizeof(AttrNumber) * numCols);
+ *groupColIdx = grpColIdx;
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 6471ad4e41b..1492df8b030 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 d0022b4cbc0..188379c9a2d 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 0cc4780807e..2a9ddfc716a 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 21afad92b82..54e84739d24 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 921bb1d671a..37a790cc3dd 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,
@@ -157,27 +157,6 @@ get_actual_tlist(List *tlist)
*****************************************************************************/
/*
- * 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 e3c9feb677c..2c4d1c7a7e8 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 $
*
*-------------------------------------------------------------------------
*/
@@ -293,7 +293,6 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
*/
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 3cca6d681fc..a5007822976 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 275d131baba..234987fb5f0 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;
-
- 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.
diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c
index 344c87920bc..4a227de2c5b 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 38464d34387..f88e936412a 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 8f365f2efbe..008402e26f3 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;
-
- 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;
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index 3b9cf8a8c9a..132f0526676 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 ab22b737e48..5a1d07ec391 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 */
- 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)
diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h
index 532be5196bf..095ee074d38 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 2c4d0ffaa72..4eea81446b2 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 b0ec64c3e32..58da94368c9 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 0ba017471d1..38ff367384b 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 fb08b708352..f0ddb9ac9dc 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 3b1aafa07d9..6b30301ea0b 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 38626290cb1..1fdb8f9890d 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);