Commit 6543d81d authored by Tom Lane's avatar Tom Lane

Restructure handling of inheritance queries so that they work with outer

joins, and clean things up a good deal at the same time.  Append plan node
no longer hacks on rangetable at runtime --- instead, all child tables are
given their own RT entries during planning.  Concept of multiple target
tables pushed up into execMain, replacing bug-prone implementation within
nodeAppend.  Planner now supports generating Append plans for inheritance
sets either at the top of the plan (the old way) or at the bottom.  Expanding
at the bottom is appropriate for tables used as sources, since they may
appear inside an outer join; but we must still expand at the top when the
target of an UPDATE or DELETE is an inheritance set, because we actually need
a different targetlist and junkfilter for each target table in that case.
Fortunately a target table can't be inside an outer join...  Bizarre mutual
recursion between union_planner and prepunion.c is gone --- in fact,
union_planner doesn't really have much to do with union queries anymore,
so I renamed it grouping_planner.
parent 609f9199
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/commands/Attic/command.c,v 1.109 2000/11/08 22:09:57 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/commands/Attic/command.c,v 1.110 2000/11/12 00:36:56 tgl Exp $
* *
* NOTES * NOTES
* The PerformAddAttribute() code, like most of the relation * The PerformAddAttribute() code, like most of the relation
...@@ -1098,13 +1098,12 @@ AlterTableAddConstraint(char *relationName, ...@@ -1098,13 +1098,12 @@ AlterTableAddConstraint(char *relationName,
case CONSTR_CHECK: case CONSTR_CHECK:
{ {
ParseState *pstate; ParseState *pstate;
bool successful = TRUE; bool successful = true;
HeapScanDesc scan; HeapScanDesc scan;
ExprContext *econtext; ExprContext *econtext;
TupleTableSlot *slot = makeNode(TupleTableSlot); TupleTableSlot *slot = makeNode(TupleTableSlot);
HeapTuple tuple; HeapTuple tuple;
RangeTblEntry *rte; RangeTblEntry *rte;
List *rtlist;
List *qual; List *qual;
List *constlist; List *constlist;
Relation rel; Relation rel;
...@@ -1112,9 +1111,9 @@ AlterTableAddConstraint(char *relationName, ...@@ -1112,9 +1111,9 @@ AlterTableAddConstraint(char *relationName,
char *name; char *name;
if (constr->name) if (constr->name)
name=constr->name; name = constr->name;
else else
name="<unnamed>"; name = "<unnamed>";
constlist = makeList1(constr); constlist = makeList1(constr);
...@@ -1169,13 +1168,6 @@ AlterTableAddConstraint(char *relationName, ...@@ -1169,13 +1168,6 @@ AlterTableAddConstraint(char *relationName,
qual = makeList1(expr); qual = makeList1(expr);
rte = makeNode(RangeTblEntry);
rte->relname = relationName;
rte->relid = RelationGetRelid(rel);
rte->eref = makeNode(Attr);
rte->eref->relname = relationName;
rtlist = makeList1(rte);
/* /*
* Scan through the rows now, making the necessary things * Scan through the rows now, making the necessary things
* for ExecQual, and then call it to evaluate the * for ExecQual, and then call it to evaluate the
...@@ -1188,10 +1180,8 @@ AlterTableAddConstraint(char *relationName, ...@@ -1188,10 +1180,8 @@ AlterTableAddConstraint(char *relationName,
slot->ttc_descIsNew = true; slot->ttc_descIsNew = true;
slot->ttc_tupleDescriptor = rel->rd_att; slot->ttc_tupleDescriptor = rel->rd_att;
slot->ttc_buffer = InvalidBuffer; slot->ttc_buffer = InvalidBuffer;
slot->ttc_whichplan = -1;
econtext = MakeExprContext(slot, CurrentMemoryContext); econtext = MakeExprContext(slot, CurrentMemoryContext);
econtext->ecxt_range_table = rtlist; /* range table */
if (!ExecQual(qual, econtext, true)) if (!ExecQual(qual, econtext, true))
{ {
successful=false; successful=false;
...@@ -1201,8 +1191,6 @@ AlterTableAddConstraint(char *relationName, ...@@ -1201,8 +1191,6 @@ AlterTableAddConstraint(char *relationName,
} }
pfree(slot); pfree(slot);
pfree(rtlist);
pfree(rte);
heap_endscan(scan); heap_endscan(scan);
heap_close(rel, NoLock); heap_close(rel, NoLock);
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/commands/copy.c,v 1.122 2000/09/06 14:15:16 petere Exp $ * $Header: /cvsroot/pgsql/src/backend/commands/copy.c,v 1.123 2000/11/12 00:36:56 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -597,7 +597,7 @@ CopyFrom(Relation rel, bool binary, bool oids, FILE *fp, ...@@ -597,7 +597,7 @@ CopyFrom(Relation rel, bool binary, bool oids, FILE *fp,
int32 ntuples, int32 ntuples,
tuples_read = 0; tuples_read = 0;
bool reading_to_eof = true; bool reading_to_eof = true;
RelationInfo *relationInfo; ResultRelInfo *resultRelInfo;
EState *estate = CreateExecutorState(); /* for ExecConstraints() */ EState *estate = CreateExecutorState(); /* for ExecConstraints() */
TupleTable tupleTable; TupleTable tupleTable;
TupleTableSlot *slot; TupleTableSlot *slot;
...@@ -609,20 +609,19 @@ CopyFrom(Relation rel, bool binary, bool oids, FILE *fp, ...@@ -609,20 +609,19 @@ CopyFrom(Relation rel, bool binary, bool oids, FILE *fp,
attr_count = tupDesc->natts; attr_count = tupDesc->natts;
/* /*
* We need a RelationInfo so we can use the regular executor's * We need a ResultRelInfo so we can use the regular executor's
* index-entry-making machinery. (There used to be a huge amount * index-entry-making machinery. (There used to be a huge amount
* of code here that basically duplicated execUtils.c ...) * of code here that basically duplicated execUtils.c ...)
*/ */
relationInfo = makeNode(RelationInfo); resultRelInfo = makeNode(ResultRelInfo);
relationInfo->ri_RangeTableIndex = 1; /* dummy */ resultRelInfo->ri_RangeTableIndex = 1; /* dummy */
relationInfo->ri_RelationDesc = rel; resultRelInfo->ri_RelationDesc = rel;
relationInfo->ri_NumIndices = 0;
relationInfo->ri_IndexRelationDescs = NULL;
relationInfo->ri_IndexRelationInfo = NULL;
ExecOpenIndices(relationInfo); ExecOpenIndices(resultRelInfo);
estate->es_result_relation_info = relationInfo; estate->es_result_relations = resultRelInfo;
estate->es_num_result_relations = 1;
estate->es_result_relation_info = resultRelInfo;
/* Set up a dummy tuple table too */ /* Set up a dummy tuple table too */
tupleTable = ExecCreateTupleTable(1); tupleTable = ExecCreateTupleTable(1);
...@@ -830,7 +829,7 @@ CopyFrom(Relation rel, bool binary, bool oids, FILE *fp, ...@@ -830,7 +829,7 @@ CopyFrom(Relation rel, bool binary, bool oids, FILE *fp,
ExecStoreTuple(tuple, slot, InvalidBuffer, false); ExecStoreTuple(tuple, slot, InvalidBuffer, false);
if (rel->rd_att->constr) if (rel->rd_att->constr)
ExecConstraints("CopyFrom", rel, slot, estate); ExecConstraints("CopyFrom", resultRelInfo, slot, estate);
/* ---------------- /* ----------------
* OK, store the tuple and create index entries for it * OK, store the tuple and create index entries for it
...@@ -838,7 +837,7 @@ CopyFrom(Relation rel, bool binary, bool oids, FILE *fp, ...@@ -838,7 +837,7 @@ CopyFrom(Relation rel, bool binary, bool oids, FILE *fp,
*/ */
heap_insert(rel, tuple); heap_insert(rel, tuple);
if (relationInfo->ri_NumIndices > 0) if (resultRelInfo->ri_NumIndices > 0)
ExecInsertIndexTuples(slot, &(tuple->t_self), estate, false); ExecInsertIndexTuples(slot, &(tuple->t_self), estate, false);
/* AFTER ROW INSERT Triggers */ /* AFTER ROW INSERT Triggers */
...@@ -886,7 +885,7 @@ CopyFrom(Relation rel, bool binary, bool oids, FILE *fp, ...@@ -886,7 +885,7 @@ CopyFrom(Relation rel, bool binary, bool oids, FILE *fp,
ExecDropTupleTable(tupleTable, true); ExecDropTupleTable(tupleTable, true);
ExecCloseIndices(relationInfo); ExecCloseIndices(resultRelInfo);
} }
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994-5, Regents of the University of California * Portions Copyright (c) 1994-5, Regents of the University of California
* *
* $Header: /cvsroot/pgsql/src/backend/commands/explain.c,v 1.61 2000/10/26 21:34:44 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/commands/explain.c,v 1.62 2000/11/12 00:36:56 tgl Exp $
* *
*/ */
...@@ -327,32 +327,18 @@ explain_outNode(StringInfo str, Plan *plan, int indent, ExplainState *es) ...@@ -327,32 +327,18 @@ explain_outNode(StringInfo str, Plan *plan, int indent, ExplainState *es)
if (IsA(plan, Append)) if (IsA(plan, Append))
{ {
Append *appendplan = (Append *) plan; Append *appendplan = (Append *) plan;
List *saved_rtable = es->rtable;
int whichplan = 0;
List *lst; List *lst;
foreach(lst, appendplan->appendplans) foreach(lst, appendplan->appendplans)
{ {
Plan *subnode = (Plan *) lfirst(lst); Plan *subnode = (Plan *) lfirst(lst);
if (appendplan->inheritrelid > 0)
{
RangeTblEntry *rtentry;
rtentry = nth(whichplan, appendplan->inheritrtable);
Assert(rtentry != NULL);
rt_store(appendplan->inheritrelid, es->rtable, rtentry);
}
for (i = 0; i < indent; i++) for (i = 0; i < indent; i++)
appendStringInfo(str, " "); appendStringInfo(str, " ");
appendStringInfo(str, " -> "); appendStringInfo(str, " -> ");
explain_outNode(str, subnode, indent + 3, es); explain_outNode(str, subnode, indent + 3, es);
whichplan++;
} }
es->rtable = saved_rtable;
} }
if (IsA(plan, SubqueryScan)) if (IsA(plan, SubqueryScan))
......
...@@ -27,7 +27,7 @@ ...@@ -27,7 +27,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.131 2000/10/26 21:35:15 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.132 2000/11/12 00:36:57 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -50,6 +50,10 @@ static TupleDesc InitPlan(CmdType operation, ...@@ -50,6 +50,10 @@ static TupleDesc InitPlan(CmdType operation,
Query *parseTree, Query *parseTree,
Plan *plan, Plan *plan,
EState *estate); EState *estate);
static void initResultRelInfo(ResultRelInfo *resultRelInfo,
Index resultRelationIndex,
List *rangeTable,
CmdType operation);
static void EndPlan(Plan *plan, EState *estate); static void EndPlan(Plan *plan, EState *estate);
static TupleTableSlot *ExecutePlan(EState *estate, Plan *plan, static TupleTableSlot *ExecutePlan(EState *estate, Plan *plan,
CmdType operation, CmdType operation,
...@@ -310,10 +314,6 @@ ExecCheckQueryPerms(CmdType operation, Query *parseTree, Plan *plan) ...@@ -310,10 +314,6 @@ ExecCheckQueryPerms(CmdType operation, Query *parseTree, Plan *plan)
* ExecCheckPlanPerms * ExecCheckPlanPerms
* Recursively scan the plan tree to check access permissions in * Recursively scan the plan tree to check access permissions in
* subplans. * subplans.
*
* We also need to look at the local rangetables in Append plan nodes,
* which is pretty bogus --- most likely, those tables should be mentioned
* in the query's main rangetable. But at the moment, they're not.
*/ */
static void static void
ExecCheckPlanPerms(Plan *plan, List *rangeTable, CmdType operation) ExecCheckPlanPerms(Plan *plan, List *rangeTable, CmdType operation)
...@@ -365,27 +365,11 @@ ExecCheckPlanPerms(Plan *plan, List *rangeTable, CmdType operation) ...@@ -365,27 +365,11 @@ ExecCheckPlanPerms(Plan *plan, List *rangeTable, CmdType operation)
Append *app = (Append *) plan; Append *app = (Append *) plan;
List *appendplans; List *appendplans;
if (app->inheritrelid > 0) foreach(appendplans, app->appendplans)
{ {
/* Append implements expansion of inheritance */ ExecCheckPlanPerms((Plan *) lfirst(appendplans),
ExecCheckRTPerms(app->inheritrtable, operation); rangeTable,
operation);
foreach(appendplans, app->appendplans)
{
ExecCheckPlanPerms((Plan *) lfirst(appendplans),
rangeTable,
operation);
}
}
else
{
/* Append implements UNION, which must be a SELECT */
foreach(appendplans, app->appendplans)
{
ExecCheckPlanPerms((Plan *) lfirst(appendplans),
rangeTable,
CMD_SELECT);
}
} }
break; break;
} }
...@@ -518,10 +502,8 @@ static TupleDesc ...@@ -518,10 +502,8 @@ static TupleDesc
InitPlan(CmdType operation, Query *parseTree, Plan *plan, EState *estate) InitPlan(CmdType operation, Query *parseTree, Plan *plan, EState *estate)
{ {
List *rangeTable; List *rangeTable;
int resultRelation;
Relation intoRelationDesc; Relation intoRelationDesc;
TupleDesc tupType; TupleDesc tupType;
List *targetList;
/* /*
* Do permissions checks. * Do permissions checks.
...@@ -532,7 +514,6 @@ InitPlan(CmdType operation, Query *parseTree, Plan *plan, EState *estate) ...@@ -532,7 +514,6 @@ InitPlan(CmdType operation, Query *parseTree, Plan *plan, EState *estate)
* get information from query descriptor * get information from query descriptor
*/ */
rangeTable = parseTree->rtable; rangeTable = parseTree->rtable;
resultRelation = parseTree->resultRelation;
/* /*
* initialize the node's execution state * initialize the node's execution state
...@@ -540,63 +521,61 @@ InitPlan(CmdType operation, Query *parseTree, Plan *plan, EState *estate) ...@@ -540,63 +521,61 @@ InitPlan(CmdType operation, Query *parseTree, Plan *plan, EState *estate)
estate->es_range_table = rangeTable; estate->es_range_table = rangeTable;
/* /*
* initialize result relation stuff * if there is a result relation, initialize result relation stuff
*/ */
if (parseTree->resultRelation != 0 && operation != CMD_SELECT)
if (resultRelation != 0 && operation != CMD_SELECT)
{ {
List *resultRelations = parseTree->resultRelations;
int numResultRelations;
ResultRelInfo *resultRelInfos;
/* if (resultRelations != NIL)
* if we have a result relation, open it and initialize the result {
* relation info stuff. /*
*/ * Multiple result relations (due to inheritance)
RelationInfo *resultRelationInfo; * parseTree->resultRelations identifies them all
Index resultRelationIndex; */
Oid resultRelationOid; ResultRelInfo *resultRelInfo;
Relation resultRelationDesc;
resultRelationIndex = resultRelation;
resultRelationOid = getrelid(resultRelationIndex, rangeTable);
resultRelationDesc = heap_open(resultRelationOid, RowExclusiveLock);
if (resultRelationDesc->rd_rel->relkind == RELKIND_SEQUENCE)
elog(ERROR, "You can't change sequence relation %s",
RelationGetRelationName(resultRelationDesc));
if (resultRelationDesc->rd_rel->relkind == RELKIND_TOASTVALUE)
elog(ERROR, "You can't change toast relation %s",
RelationGetRelationName(resultRelationDesc));
if (resultRelationDesc->rd_rel->relkind == RELKIND_VIEW)
elog(ERROR, "You can't change view relation %s",
RelationGetRelationName(resultRelationDesc));
resultRelationInfo = makeNode(RelationInfo);
resultRelationInfo->ri_RangeTableIndex = resultRelationIndex;
resultRelationInfo->ri_RelationDesc = resultRelationDesc;
resultRelationInfo->ri_NumIndices = 0;
resultRelationInfo->ri_IndexRelationDescs = NULL;
resultRelationInfo->ri_IndexRelationInfo = NULL;
/* numResultRelations = length(resultRelations);
* If there are indices on the result relation, open them and save resultRelInfos = (ResultRelInfo *)
* descriptors in the result relation info, so that we can add new palloc(numResultRelations * sizeof(ResultRelInfo));
* index entries for the tuples we add/update. We need not do resultRelInfo = resultRelInfos;
* this for a DELETE, however, since deletion doesn't affect while (resultRelations != NIL)
* indexes. {
*/ initResultRelInfo(resultRelInfo,
if (resultRelationDesc->rd_rel->relhasindex && lfirsti(resultRelations),
operation != CMD_DELETE) rangeTable,
ExecOpenIndices(resultRelationInfo); operation);
resultRelInfo++;
resultRelations = lnext(resultRelations);
}
}
else
{
/*
* Single result relation identified by parseTree->resultRelation
*/
numResultRelations = 1;
resultRelInfos = (ResultRelInfo *) palloc(sizeof(ResultRelInfo));
initResultRelInfo(resultRelInfos,
parseTree->resultRelation,
rangeTable,
operation);
}
estate->es_result_relation_info = resultRelationInfo; estate->es_result_relations = resultRelInfos;
estate->es_num_result_relations = numResultRelations;
/* Initialize to first or only result rel */
estate->es_result_relation_info = resultRelInfos;
} }
else else
{ {
/* /*
* if no result relation, then set state appropriately * if no result relation, then set state appropriately
*/ */
estate->es_result_relations = NULL;
estate->es_num_result_relations = 0;
estate->es_result_relation_info = NULL; estate->es_result_relation_info = NULL;
} }
...@@ -642,19 +621,17 @@ InitPlan(CmdType operation, Query *parseTree, Plan *plan, EState *estate) ...@@ -642,19 +621,17 @@ InitPlan(CmdType operation, Query *parseTree, Plan *plan, EState *estate)
ExecInitNode(plan, estate, NULL); ExecInitNode(plan, estate, NULL);
/* /*
* get the tuple descriptor describing the type of tuples to return.. * Get the tuple descriptor describing the type of tuples to return.
* (this is especially important if we are creating a relation with * (this is especially important if we are creating a relation with
* "retrieve into") * "retrieve into")
*/ */
tupType = ExecGetTupType(plan); /* tuple descriptor */ tupType = ExecGetTupType(plan); /* tuple descriptor */
targetList = plan->targetlist;
/* /*
* Now that we have the target list, initialize the junk filter if * Initialize the junk filter if needed. SELECT and INSERT queries need
* needed. SELECT and INSERT queries need a filter if there are any * a filter if there are any junk attrs in the tlist. UPDATE and
* junk attrs in the tlist. UPDATE and DELETE always need one, since * DELETE always need one, since there's always a junk 'ctid' attribute
* there's always a junk 'ctid' attribute present --- no need to look * present --- no need to look first.
* first.
*/ */
{ {
bool junk_filter_needed = false; bool junk_filter_needed = false;
...@@ -664,7 +641,7 @@ InitPlan(CmdType operation, Query *parseTree, Plan *plan, EState *estate) ...@@ -664,7 +641,7 @@ InitPlan(CmdType operation, Query *parseTree, Plan *plan, EState *estate)
{ {
case CMD_SELECT: case CMD_SELECT:
case CMD_INSERT: case CMD_INSERT:
foreach(tlist, targetList) foreach(tlist, plan->targetlist)
{ {
TargetEntry *tle = (TargetEntry *) lfirst(tlist); TargetEntry *tle = (TargetEntry *) lfirst(tlist);
...@@ -685,12 +662,55 @@ InitPlan(CmdType operation, Query *parseTree, Plan *plan, EState *estate) ...@@ -685,12 +662,55 @@ InitPlan(CmdType operation, Query *parseTree, Plan *plan, EState *estate)
if (junk_filter_needed) if (junk_filter_needed)
{ {
JunkFilter *j = ExecInitJunkFilter(targetList, tupType); /*
* If there are multiple result relations, each one needs
* its own junk filter. Note this is only possible for
* UPDATE/DELETE, so we can't be fooled by some needing
* a filter and some not.
*/
if (parseTree->resultRelations != NIL)
{
List *subplans;
ResultRelInfo *resultRelInfo;
/* Top plan had better be an Append here. */
Assert(IsA(plan, Append));
Assert(((Append *) plan)->isTarget);
subplans = ((Append *) plan)->appendplans;
Assert(length(subplans) == estate->es_num_result_relations);
resultRelInfo = estate->es_result_relations;
while (subplans != NIL)
{
Plan *subplan = (Plan *) lfirst(subplans);
JunkFilter *j;
j = ExecInitJunkFilter(subplan->targetlist,
ExecGetTupType(subplan));
resultRelInfo->ri_junkFilter = j;
resultRelInfo++;
subplans = lnext(subplans);
}
/*
* Set active junkfilter too; at this point ExecInitAppend
* has already selected an active result relation...
*/
estate->es_junkFilter =
estate->es_result_relation_info->ri_junkFilter;
}
else
{
/* Normal case with just one JunkFilter */
JunkFilter *j = ExecInitJunkFilter(plan->targetlist,
tupType);
estate->es_junkFilter = j; estate->es_junkFilter = j;
if (estate->es_result_relation_info)
estate->es_result_relation_info->ri_junkFilter = j;
if (operation == CMD_SELECT) /* For SELECT, want to return the cleaned tuple type */
tupType = j->jf_cleanTupType; if (operation == CMD_SELECT)
tupType = j->jf_cleanTupType;
}
} }
else else
estate->es_junkFilter = NULL; estate->es_junkFilter = NULL;
...@@ -762,6 +782,59 @@ InitPlan(CmdType operation, Query *parseTree, Plan *plan, EState *estate) ...@@ -762,6 +782,59 @@ InitPlan(CmdType operation, Query *parseTree, Plan *plan, EState *estate)
return tupType; return tupType;
} }
/*
* Initialize ResultRelInfo data for one result relation
*/
static void
initResultRelInfo(ResultRelInfo *resultRelInfo,
Index resultRelationIndex,
List *rangeTable,
CmdType operation)
{
Oid resultRelationOid;
Relation resultRelationDesc;
resultRelationOid = getrelid(resultRelationIndex, rangeTable);
resultRelationDesc = heap_open(resultRelationOid, RowExclusiveLock);
switch (resultRelationDesc->rd_rel->relkind)
{
case RELKIND_SEQUENCE:
elog(ERROR, "You can't change sequence relation %s",
RelationGetRelationName(resultRelationDesc));
break;
case RELKIND_TOASTVALUE:
elog(ERROR, "You can't change toast relation %s",
RelationGetRelationName(resultRelationDesc));
break;
case RELKIND_VIEW:
elog(ERROR, "You can't change view relation %s",
RelationGetRelationName(resultRelationDesc));
break;
}
MemSet(resultRelInfo, 0, sizeof(ResultRelInfo));
resultRelInfo->type = T_ResultRelInfo;
resultRelInfo->ri_RangeTableIndex = resultRelationIndex;
resultRelInfo->ri_RelationDesc = resultRelationDesc;
resultRelInfo->ri_NumIndices = 0;
resultRelInfo->ri_IndexRelationDescs = NULL;
resultRelInfo->ri_IndexRelationInfo = NULL;
resultRelInfo->ri_ConstraintExprs = NULL;
resultRelInfo->ri_junkFilter = NULL;
/*
* If there are indices on the result relation, open them and save
* descriptors in the result relation info, so that we can add new
* index entries for the tuples we add/update. We need not do
* this for a DELETE, however, since deletion doesn't affect
* indexes.
*/
if (resultRelationDesc->rd_rel->relhasindex &&
operation != CMD_DELETE)
ExecOpenIndices(resultRelInfo);
}
/* ---------------------------------------------------------------- /* ----------------------------------------------------------------
* EndPlan * EndPlan
* *
...@@ -771,7 +844,8 @@ InitPlan(CmdType operation, Query *parseTree, Plan *plan, EState *estate) ...@@ -771,7 +844,8 @@ InitPlan(CmdType operation, Query *parseTree, Plan *plan, EState *estate)
static void static void
EndPlan(Plan *plan, EState *estate) EndPlan(Plan *plan, EState *estate)
{ {
RelationInfo *resultRelationInfo; ResultRelInfo *resultRelInfo;
int i;
List *l; List *l;
/* /*
...@@ -792,16 +866,16 @@ EndPlan(Plan *plan, EState *estate) ...@@ -792,16 +866,16 @@ EndPlan(Plan *plan, EState *estate)
estate->es_tupleTable = NULL; estate->es_tupleTable = NULL;
/* /*
* close the result relation if necessary, but hold lock on it * close the result relation(s) if any, but hold locks
* until xact commit. NB: must not do this till after ExecEndNode(), * until xact commit.
* see nodeAppend.c ...
*/ */
resultRelationInfo = estate->es_result_relation_info; resultRelInfo = estate->es_result_relations;
if (resultRelationInfo != NULL) for (i = estate->es_num_result_relations; i > 0; i--)
{ {
heap_close(resultRelationInfo->ri_RelationDesc, NoLock); /* Close indices and then the relation itself */
/* close indices on the result relation, too */ ExecCloseIndices(resultRelInfo);
ExecCloseIndices(resultRelationInfo); heap_close(resultRelInfo->ri_RelationDesc, NoLock);
resultRelInfo++;
} }
/* /*
...@@ -1116,7 +1190,7 @@ ExecAppend(TupleTableSlot *slot, ...@@ -1116,7 +1190,7 @@ ExecAppend(TupleTableSlot *slot,
EState *estate) EState *estate)
{ {
HeapTuple tuple; HeapTuple tuple;
RelationInfo *resultRelationInfo; ResultRelInfo *resultRelInfo;
Relation resultRelationDesc; Relation resultRelationDesc;
int numIndices; int numIndices;
Oid newId; Oid newId;
...@@ -1127,10 +1201,10 @@ ExecAppend(TupleTableSlot *slot, ...@@ -1127,10 +1201,10 @@ ExecAppend(TupleTableSlot *slot,
tuple = slot->val; tuple = slot->val;
/* /*
* get information on the result relation * get information on the (current) result relation
*/ */
resultRelationInfo = estate->es_result_relation_info; resultRelInfo = estate->es_result_relation_info;
resultRelationDesc = resultRelationInfo->ri_RelationDesc; resultRelationDesc = resultRelInfo->ri_RelationDesc;
/* BEFORE ROW INSERT Triggers */ /* BEFORE ROW INSERT Triggers */
if (resultRelationDesc->trigdesc && if (resultRelationDesc->trigdesc &&
...@@ -1154,9 +1228,8 @@ ExecAppend(TupleTableSlot *slot, ...@@ -1154,9 +1228,8 @@ ExecAppend(TupleTableSlot *slot,
/* /*
* Check the constraints of the tuple * Check the constraints of the tuple
*/ */
if (resultRelationDesc->rd_att->constr) if (resultRelationDesc->rd_att->constr)
ExecConstraints("ExecAppend", resultRelationDesc, slot, estate); ExecConstraints("ExecAppend", resultRelInfo, slot, estate);
/* /*
* insert the tuple * insert the tuple
...@@ -1174,7 +1247,7 @@ ExecAppend(TupleTableSlot *slot, ...@@ -1174,7 +1247,7 @@ ExecAppend(TupleTableSlot *slot,
* the tupleid of the new tuple is placed in the new tuple's t_ctid * the tupleid of the new tuple is placed in the new tuple's t_ctid
* field. * field.
*/ */
numIndices = resultRelationInfo->ri_NumIndices; numIndices = resultRelInfo->ri_NumIndices;
if (numIndices > 0) if (numIndices > 0)
ExecInsertIndexTuples(slot, &(tuple->t_self), estate, false); ExecInsertIndexTuples(slot, &(tuple->t_self), estate, false);
...@@ -1195,16 +1268,16 @@ ExecDelete(TupleTableSlot *slot, ...@@ -1195,16 +1268,16 @@ ExecDelete(TupleTableSlot *slot,
ItemPointer tupleid, ItemPointer tupleid,
EState *estate) EState *estate)
{ {
RelationInfo *resultRelationInfo; ResultRelInfo *resultRelInfo;
Relation resultRelationDesc; Relation resultRelationDesc;
ItemPointerData ctid; ItemPointerData ctid;
int result; int result;
/* /*
* get the result relation information * get information on the (current) result relation
*/ */
resultRelationInfo = estate->es_result_relation_info; resultRelInfo = estate->es_result_relation_info;
resultRelationDesc = resultRelationInfo->ri_RelationDesc; resultRelationDesc = resultRelInfo->ri_RelationDesc;
/* BEFORE ROW DELETE Triggers */ /* BEFORE ROW DELETE Triggers */
if (resultRelationDesc->trigdesc && if (resultRelationDesc->trigdesc &&
...@@ -1237,7 +1310,7 @@ ldelete:; ...@@ -1237,7 +1310,7 @@ ldelete:;
else if (!(ItemPointerEquals(tupleid, &ctid))) else if (!(ItemPointerEquals(tupleid, &ctid)))
{ {
TupleTableSlot *epqslot = EvalPlanQual(estate, TupleTableSlot *epqslot = EvalPlanQual(estate,
resultRelationInfo->ri_RangeTableIndex, &ctid); resultRelInfo->ri_RangeTableIndex, &ctid);
if (!TupIsNull(epqslot)) if (!TupIsNull(epqslot))
{ {
...@@ -1287,7 +1360,7 @@ ExecReplace(TupleTableSlot *slot, ...@@ -1287,7 +1360,7 @@ ExecReplace(TupleTableSlot *slot,
EState *estate) EState *estate)
{ {
HeapTuple tuple; HeapTuple tuple;
RelationInfo *resultRelationInfo; ResultRelInfo *resultRelInfo;
Relation resultRelationDesc; Relation resultRelationDesc;
ItemPointerData ctid; ItemPointerData ctid;
int result; int result;
...@@ -1308,10 +1381,10 @@ ExecReplace(TupleTableSlot *slot, ...@@ -1308,10 +1381,10 @@ ExecReplace(TupleTableSlot *slot,
tuple = slot->val; tuple = slot->val;
/* /*
* get the result relation information * get information on the (current) result relation
*/ */
resultRelationInfo = estate->es_result_relation_info; resultRelInfo = estate->es_result_relation_info;
resultRelationDesc = resultRelationInfo->ri_RelationDesc; resultRelationDesc = resultRelInfo->ri_RelationDesc;
/* BEFORE ROW UPDATE Triggers */ /* BEFORE ROW UPDATE Triggers */
if (resultRelationDesc->trigdesc && if (resultRelationDesc->trigdesc &&
...@@ -1335,9 +1408,8 @@ ExecReplace(TupleTableSlot *slot, ...@@ -1335,9 +1408,8 @@ ExecReplace(TupleTableSlot *slot,
/* /*
* Check the constraints of the tuple * Check the constraints of the tuple
*/ */
if (resultRelationDesc->rd_att->constr) if (resultRelationDesc->rd_att->constr)
ExecConstraints("ExecReplace", resultRelationDesc, slot, estate); ExecConstraints("ExecReplace", resultRelInfo, slot, estate);
/* /*
* replace the heap tuple * replace the heap tuple
...@@ -1358,7 +1430,7 @@ lreplace:; ...@@ -1358,7 +1430,7 @@ lreplace:;
else if (!(ItemPointerEquals(tupleid, &ctid))) else if (!(ItemPointerEquals(tupleid, &ctid)))
{ {
TupleTableSlot *epqslot = EvalPlanQual(estate, TupleTableSlot *epqslot = EvalPlanQual(estate,
resultRelationInfo->ri_RangeTableIndex, &ctid); resultRelInfo->ri_RangeTableIndex, &ctid);
if (!TupIsNull(epqslot)) if (!TupIsNull(epqslot))
{ {
...@@ -1396,7 +1468,7 @@ lreplace:; ...@@ -1396,7 +1468,7 @@ lreplace:;
* there. * there.
*/ */
numIndices = resultRelationInfo->ri_NumIndices; numIndices = resultRelInfo->ri_NumIndices;
if (numIndices > 0) if (numIndices > 0)
ExecInsertIndexTuples(slot, &(tuple->t_self), estate, true); ExecInsertIndexTuples(slot, &(tuple->t_self), estate, true);
...@@ -1406,8 +1478,10 @@ lreplace:; ...@@ -1406,8 +1478,10 @@ lreplace:;
} }
static char * static char *
ExecRelCheck(Relation rel, TupleTableSlot *slot, EState *estate) ExecRelCheck(ResultRelInfo *resultRelInfo,
TupleTableSlot *slot, EState *estate)
{ {
Relation rel = resultRelInfo->ri_RelationDesc;
int ncheck = rel->rd_att->constr->num_check; int ncheck = rel->rd_att->constr->num_check;
ConstrCheck *check = rel->rd_att->constr->check; ConstrCheck *check = rel->rd_att->constr->check;
ExprContext *econtext; ExprContext *econtext;
...@@ -1415,6 +1489,24 @@ ExecRelCheck(Relation rel, TupleTableSlot *slot, EState *estate) ...@@ -1415,6 +1489,24 @@ ExecRelCheck(Relation rel, TupleTableSlot *slot, EState *estate)
List *qual; List *qual;
int i; int i;
/*
* If first time through for this result relation, build expression
* nodetrees for rel's constraint expressions. Keep them in the
* per-query memory context so they'll survive throughout the query.
*/
if (resultRelInfo->ri_ConstraintExprs == NULL)
{
oldContext = MemoryContextSwitchTo(estate->es_query_cxt);
resultRelInfo->ri_ConstraintExprs =
(List **) palloc(ncheck * sizeof(List *));
for (i = 0; i < ncheck; i++)
{
qual = (List *) stringToNode(check[i].ccbin);
resultRelInfo->ri_ConstraintExprs[i] = qual;
}
MemoryContextSwitchTo(oldContext);
}
/* /*
* We will use the EState's per-tuple context for evaluating constraint * We will use the EState's per-tuple context for evaluating constraint
* expressions. Create it if it's not already there; if it is, reset it * expressions. Create it if it's not already there; if it is, reset it
...@@ -1431,58 +1523,13 @@ ExecRelCheck(Relation rel, TupleTableSlot *slot, EState *estate) ...@@ -1431,58 +1523,13 @@ ExecRelCheck(Relation rel, TupleTableSlot *slot, EState *estate)
else else
ResetExprContext(econtext); ResetExprContext(econtext);
/*
* If first time through for current result relation, set up econtext's
* range table to refer to result rel, and build expression nodetrees
* for rel's constraint expressions. All this stuff is kept in the
* per-query memory context so it will still be here next time through.
*
* NOTE: if there are multiple result relations (eg, due to inheritance)
* then we leak storage for prior rel's expressions and rangetable.
* This should not be a big problem as long as result rels are processed
* sequentially within a command.
*/
if (econtext->ecxt_range_table == NIL ||
getrelid(1, econtext->ecxt_range_table) != RelationGetRelid(rel))
{
RangeTblEntry *rte;
/*
* Make sure expressions, etc are placed in appropriate context.
*/
oldContext = MemoryContextSwitchTo(estate->es_query_cxt);
rte = makeNode(RangeTblEntry);
rte->relname = RelationGetRelationName(rel);
rte->relid = RelationGetRelid(rel);
rte->eref = makeNode(Attr);
rte->eref->relname = rte->relname;
/* other fields won't be used, leave them zero */
/* Set up single-entry range table */
econtext->ecxt_range_table = makeList1(rte);
estate->es_result_relation_constraints =
(List **) palloc(ncheck * sizeof(List *));
for (i = 0; i < ncheck; i++)
{
qual = (List *) stringToNode(check[i].ccbin);
estate->es_result_relation_constraints[i] = qual;
}
/* Done with building long-lived items */
MemoryContextSwitchTo(oldContext);
}
/* Arrange for econtext's scan tuple to be the tuple under test */ /* Arrange for econtext's scan tuple to be the tuple under test */
econtext->ecxt_scantuple = slot; econtext->ecxt_scantuple = slot;
/* And evaluate the constraints */ /* And evaluate the constraints */
for (i = 0; i < ncheck; i++) for (i = 0; i < ncheck; i++)
{ {
qual = estate->es_result_relation_constraints[i]; qual = resultRelInfo->ri_ConstraintExprs[i];
/* /*
* NOTE: SQL92 specifies that a NULL result from a constraint * NOTE: SQL92 specifies that a NULL result from a constraint
...@@ -1498,9 +1545,10 @@ ExecRelCheck(Relation rel, TupleTableSlot *slot, EState *estate) ...@@ -1498,9 +1545,10 @@ ExecRelCheck(Relation rel, TupleTableSlot *slot, EState *estate)
} }
void void
ExecConstraints(char *caller, Relation rel, ExecConstraints(char *caller, ResultRelInfo *resultRelInfo,
TupleTableSlot *slot, EState *estate) TupleTableSlot *slot, EState *estate)
{ {
Relation rel = resultRelInfo->ri_RelationDesc;
HeapTuple tuple = slot->val; HeapTuple tuple = slot->val;
TupleConstr *constr = rel->rd_att->constr; TupleConstr *constr = rel->rd_att->constr;
...@@ -1524,7 +1572,7 @@ ExecConstraints(char *caller, Relation rel, ...@@ -1524,7 +1572,7 @@ ExecConstraints(char *caller, Relation rel,
{ {
char *failed; char *failed;
if ((failed = ExecRelCheck(rel, slot, estate)) != NULL) if ((failed = ExecRelCheck(resultRelInfo, slot, estate)) != NULL)
elog(ERROR, "%s: rejected due to CHECK constraint %s", elog(ERROR, "%s: rejected due to CHECK constraint %s",
caller, failed); caller, failed);
} }
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.80 2000/08/24 23:34:09 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.81 2000/11/12 00:36:57 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -341,7 +341,6 @@ ExecEvalVar(Var *variable, ExprContext *econtext, bool *isNull) ...@@ -341,7 +341,6 @@ ExecEvalVar(Var *variable, ExprContext *econtext, bool *isNull)
tempSlot->ttc_descIsNew = true; tempSlot->ttc_descIsNew = true;
tempSlot->ttc_tupleDescriptor = (TupleDesc) NULL; tempSlot->ttc_tupleDescriptor = (TupleDesc) NULL;
tempSlot->ttc_buffer = InvalidBuffer; tempSlot->ttc_buffer = InvalidBuffer;
tempSlot->ttc_whichplan = -1;
tup = heap_copytuple(heapTuple); tup = heap_copytuple(heapTuple);
td = CreateTupleDescCopy(tuple_type); td = CreateTupleDescCopy(tuple_type);
......
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/executor/execTuples.c,v 1.42 2000/10/26 21:35:15 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/executor/execTuples.c,v 1.43 2000/11/12 00:36:57 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -303,7 +303,6 @@ ExecAllocTableSlot(TupleTable table) ...@@ -303,7 +303,6 @@ ExecAllocTableSlot(TupleTable table)
slot->ttc_descIsNew = true; slot->ttc_descIsNew = true;
slot->ttc_tupleDescriptor = (TupleDesc) NULL; slot->ttc_tupleDescriptor = (TupleDesc) NULL;
slot->ttc_buffer = InvalidBuffer; slot->ttc_buffer = InvalidBuffer;
slot->ttc_whichplan = -1;
return slot; return slot;
} }
...@@ -675,20 +674,11 @@ NodeGetResultTupleSlot(Plan *node) ...@@ -675,20 +674,11 @@ NodeGetResultTupleSlot(Plan *node)
case T_Append: case T_Append:
{ {
Append *n = (Append *) node; AppendState *appendstate = ((Append *) node)->appendstate;
AppendState *appendstate;
List *appendplans; slot = appendstate->cstate.cs_ResultTupleSlot;
int whichplan;
Plan *subplan;
appendstate = n->appendstate;
appendplans = n->appendplans;
whichplan = appendstate->as_whichplan;
subplan = (Plan *) nth(whichplan, appendplans);
slot = NodeGetResultTupleSlot(subplan);
break;
} }
break;
case T_IndexScan: case T_IndexScan:
{ {
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/executor/execUtils.c,v 1.67 2000/10/05 19:48:25 momjian Exp $ * $Header: /cvsroot/pgsql/src/backend/executor/execUtils.c,v 1.68 2000/11/12 00:36:57 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -172,7 +172,6 @@ ExecAssignExprContext(EState *estate, CommonState *commonstate) ...@@ -172,7 +172,6 @@ ExecAssignExprContext(EState *estate, CommonState *commonstate)
econtext->ecxt_param_list_info = estate->es_param_list_info; econtext->ecxt_param_list_info = estate->es_param_list_info;
econtext->ecxt_aggvalues = NULL; econtext->ecxt_aggvalues = NULL;
econtext->ecxt_aggnulls = NULL; econtext->ecxt_aggnulls = NULL;
econtext->ecxt_range_table = estate->es_range_table;
commonstate->cs_ExprContext = econtext; commonstate->cs_ExprContext = econtext;
} }
...@@ -215,7 +214,6 @@ MakeExprContext(TupleTableSlot *slot, ...@@ -215,7 +214,6 @@ MakeExprContext(TupleTableSlot *slot,
econtext->ecxt_param_list_info = NULL; econtext->ecxt_param_list_info = NULL;
econtext->ecxt_aggvalues = NULL; econtext->ecxt_aggvalues = NULL;
econtext->ecxt_aggnulls = NULL; econtext->ecxt_aggnulls = NULL;
econtext->ecxt_range_table = NIL;
return econtext; return econtext;
} }
...@@ -649,10 +647,10 @@ QueryDescGetTypeInfo(QueryDesc *queryDesc) ...@@ -649,10 +647,10 @@ QueryDescGetTypeInfo(QueryDesc *queryDesc)
* ExecOpenIndices * ExecOpenIndices
* *
* Find the indices associated with a result relation, open them, * Find the indices associated with a result relation, open them,
* and save information about them in the result RelationInfo. * and save information about them in the result ResultRelInfo.
* *
* At entry, caller has already opened and locked * At entry, caller has already opened and locked
* resultRelationInfo->ri_RelationDesc. * resultRelInfo->ri_RelationDesc.
* *
* This used to be horribly ugly code, and slow too because it * This used to be horribly ugly code, and slow too because it
* did a sequential scan of pg_index. Now we rely on the relcache * did a sequential scan of pg_index. Now we rely on the relcache
...@@ -662,9 +660,9 @@ QueryDescGetTypeInfo(QueryDesc *queryDesc) ...@@ -662,9 +660,9 @@ QueryDescGetTypeInfo(QueryDesc *queryDesc)
* ---------------------------------------------------------------- * ----------------------------------------------------------------
*/ */
void void
ExecOpenIndices(RelationInfo *resultRelationInfo) ExecOpenIndices(ResultRelInfo *resultRelInfo)
{ {
Relation resultRelation = resultRelationInfo->ri_RelationDesc; Relation resultRelation = resultRelInfo->ri_RelationDesc;
List *indexoidlist, List *indexoidlist,
*indexoidscan; *indexoidscan;
int len, int len,
...@@ -672,7 +670,7 @@ ExecOpenIndices(RelationInfo *resultRelationInfo) ...@@ -672,7 +670,7 @@ ExecOpenIndices(RelationInfo *resultRelationInfo)
RelationPtr relationDescs; RelationPtr relationDescs;
IndexInfo **indexInfoArray; IndexInfo **indexInfoArray;
resultRelationInfo->ri_NumIndices = 0; resultRelInfo->ri_NumIndices = 0;
/* checks for disabled indexes */ /* checks for disabled indexes */
if (! RelationGetForm(resultRelation)->relhasindex) if (! RelationGetForm(resultRelation)->relhasindex)
...@@ -697,9 +695,9 @@ ExecOpenIndices(RelationInfo *resultRelationInfo) ...@@ -697,9 +695,9 @@ ExecOpenIndices(RelationInfo *resultRelationInfo)
relationDescs = (RelationPtr) palloc(len * sizeof(Relation)); relationDescs = (RelationPtr) palloc(len * sizeof(Relation));
indexInfoArray = (IndexInfo **) palloc(len * sizeof(IndexInfo *)); indexInfoArray = (IndexInfo **) palloc(len * sizeof(IndexInfo *));
resultRelationInfo->ri_NumIndices = len; resultRelInfo->ri_NumIndices = len;
resultRelationInfo->ri_IndexRelationDescs = relationDescs; resultRelInfo->ri_IndexRelationDescs = relationDescs;
resultRelationInfo->ri_IndexRelationInfo = indexInfoArray; resultRelInfo->ri_IndexRelationInfo = indexInfoArray;
/* ---------------- /* ----------------
* For each index, open the index relation and save pg_index info. * For each index, open the index relation and save pg_index info.
...@@ -765,18 +763,18 @@ ExecOpenIndices(RelationInfo *resultRelationInfo) ...@@ -765,18 +763,18 @@ ExecOpenIndices(RelationInfo *resultRelationInfo)
/* ---------------------------------------------------------------- /* ----------------------------------------------------------------
* ExecCloseIndices * ExecCloseIndices
* *
* Close the index relations stored in resultRelationInfo * Close the index relations stored in resultRelInfo
* ---------------------------------------------------------------- * ----------------------------------------------------------------
*/ */
void void
ExecCloseIndices(RelationInfo *resultRelationInfo) ExecCloseIndices(ResultRelInfo *resultRelInfo)
{ {
int i; int i;
int numIndices; int numIndices;
RelationPtr relationDescs; RelationPtr relationDescs;
numIndices = resultRelationInfo->ri_NumIndices; numIndices = resultRelInfo->ri_NumIndices;
relationDescs = resultRelationInfo->ri_IndexRelationDescs; relationDescs = resultRelInfo->ri_IndexRelationDescs;
for (i = 0; i < numIndices; i++) for (i = 0; i < numIndices; i++)
{ {
...@@ -817,7 +815,7 @@ ExecInsertIndexTuples(TupleTableSlot *slot, ...@@ -817,7 +815,7 @@ ExecInsertIndexTuples(TupleTableSlot *slot,
bool is_update) bool is_update)
{ {
HeapTuple heapTuple; HeapTuple heapTuple;
RelationInfo *resultRelationInfo; ResultRelInfo *resultRelInfo;
int i; int i;
int numIndices; int numIndices;
RelationPtr relationDescs; RelationPtr relationDescs;
...@@ -833,11 +831,11 @@ ExecInsertIndexTuples(TupleTableSlot *slot, ...@@ -833,11 +831,11 @@ ExecInsertIndexTuples(TupleTableSlot *slot,
/* /*
* Get information from the result relation info structure. * Get information from the result relation info structure.
*/ */
resultRelationInfo = estate->es_result_relation_info; resultRelInfo = estate->es_result_relation_info;
numIndices = resultRelationInfo->ri_NumIndices; numIndices = resultRelInfo->ri_NumIndices;
relationDescs = resultRelationInfo->ri_IndexRelationDescs; relationDescs = resultRelInfo->ri_IndexRelationDescs;
indexInfoArray = resultRelationInfo->ri_IndexRelationInfo; indexInfoArray = resultRelInfo->ri_IndexRelationInfo;
heapRelation = resultRelationInfo->ri_RelationDesc; heapRelation = resultRelInfo->ri_RelationDesc;
heapDescriptor = RelationGetDescr(heapRelation); heapDescriptor = RelationGetDescr(heapRelation);
/* /*
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/executor/functions.c,v 1.39 2000/10/26 21:35:15 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/executor/functions.c,v 1.40 2000/11/12 00:36:57 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -253,7 +253,6 @@ init_sql_fcache(FmgrInfo *finfo) ...@@ -253,7 +253,6 @@ init_sql_fcache(FmgrInfo *finfo)
slot->ttc_descIsNew = true; slot->ttc_descIsNew = true;
slot->ttc_tupleDescriptor = (TupleDesc) NULL; slot->ttc_tupleDescriptor = (TupleDesc) NULL;
slot->ttc_buffer = InvalidBuffer; slot->ttc_buffer = InvalidBuffer;
slot->ttc_whichplan = -1;
fcache->funcSlot = slot; fcache->funcSlot = slot;
} }
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/executor/nodeAppend.c,v 1.37 2000/11/09 18:12:53 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/executor/nodeAppend.c,v 1.38 2000/11/12 00:36:57 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -36,8 +36,8 @@ ...@@ -36,8 +36,8 @@
* nil nil ... ... ... * nil nil ... ... ...
* subplans * subplans
* *
* Append nodes are currently used for unions, and to support inheritance * Append nodes are currently used for unions, and to support
* queries, where several relations need to be scanned. * inheritance queries, where several relations need to be scanned.
* For example, in our standard person/student/employee/student-emp * For example, in our standard person/student/employee/student-emp
* example, where student and employee inherit from person * example, where student and employee inherit from person
* and student-emp inherits from student and employee, the * and student-emp inherits from student and employee, the
...@@ -54,8 +54,8 @@ ...@@ -54,8 +54,8 @@
* | | | | * | | | |
* person employee student student-emp * person employee student student-emp
*/ */
#include "postgres.h"
#include "postgres.h"
#include "access/heapam.h" #include "access/heapam.h"
#include "executor/execdebug.h" #include "executor/execdebug.h"
...@@ -78,12 +78,8 @@ exec_append_initialize_next(Append *node) ...@@ -78,12 +78,8 @@ exec_append_initialize_next(Append *node)
{ {
EState *estate; EState *estate;
AppendState *appendstate; AppendState *appendstate;
TupleTableSlot *result_slot;
List *rangeTable;
int whichplan; int whichplan;
int nplans; int nplans;
List *inheritrtable;
RangeTblEntry *rtentry;
/* ---------------- /* ----------------
* get information from the append node * get information from the append node
...@@ -91,12 +87,8 @@ exec_append_initialize_next(Append *node) ...@@ -91,12 +87,8 @@ exec_append_initialize_next(Append *node)
*/ */
estate = node->plan.state; estate = node->plan.state;
appendstate = node->appendstate; appendstate = node->appendstate;
result_slot = appendstate->cstate.cs_ResultTupleSlot;
rangeTable = estate->es_range_table;
whichplan = appendstate->as_whichplan; whichplan = appendstate->as_whichplan;
nplans = appendstate->as_nplans; nplans = appendstate->as_nplans;
inheritrtable = node->inheritrtable;
if (whichplan < 0) if (whichplan < 0)
{ {
...@@ -110,7 +102,6 @@ exec_append_initialize_next(Append *node) ...@@ -110,7 +102,6 @@ exec_append_initialize_next(Append *node)
*/ */
appendstate->as_whichplan = 0; appendstate->as_whichplan = 0;
return FALSE; return FALSE;
} }
else if (whichplan >= nplans) else if (whichplan >= nplans)
{ {
...@@ -121,37 +112,25 @@ exec_append_initialize_next(Append *node) ...@@ -121,37 +112,25 @@ exec_append_initialize_next(Append *node)
*/ */
appendstate->as_whichplan = nplans - 1; appendstate->as_whichplan = nplans - 1;
return FALSE; return FALSE;
} }
else else
{ {
/* ---------------- /* ----------------
* initialize the scan * initialize the scan
* (and update the range table appropriately)
* *
* (doesn't this leave the range table hosed for anybody upstream * If we are controlling the target relation, select the proper
* of the Append node??? - jolly ) * active ResultRelInfo and junk filter for this target.
* ---------------- * ----------------
*/ */
if (node->inheritrelid > 0) if (node->isTarget)
{ {
rtentry = nth(whichplan, inheritrtable); Assert(whichplan < estate->es_num_result_relations);
Assert(rtentry != NULL); estate->es_result_relation_info =
rt_store(node->inheritrelid, rangeTable, rtentry); estate->es_result_relations + whichplan;
estate->es_junkFilter =
estate->es_result_relation_info->ri_junkFilter;
} }
if (appendstate->as_junkFilter_list)
{
estate->es_junkFilter = (JunkFilter *) nth(whichplan,
appendstate->as_junkFilter_list);
}
if (appendstate->as_result_relation_info_list)
{
estate->es_result_relation_info = (RelationInfo *) nth(whichplan,
appendstate->as_result_relation_info_list);
}
result_slot->ttc_whichplan = whichplan;
return TRUE; return TRUE;
} }
} }
...@@ -176,14 +155,10 @@ ExecInitAppend(Append *node, EState *estate, Plan *parent) ...@@ -176,14 +155,10 @@ ExecInitAppend(Append *node, EState *estate, Plan *parent)
{ {
AppendState *appendstate; AppendState *appendstate;
int nplans; int nplans;
List *inheritrtable;
List *appendplans; List *appendplans;
bool *initialized; bool *initialized;
int i; int i;
Plan *initNode; Plan *initNode;
List *junkList;
RelationInfo *es_rri = estate->es_result_relation_info;
bool inherited_result_rel = false;
CXT1_printf("ExecInitAppend: context is %d\n", CurrentMemoryContext); CXT1_printf("ExecInitAppend: context is %d\n", CurrentMemoryContext);
...@@ -196,7 +171,6 @@ ExecInitAppend(Append *node, EState *estate, Plan *parent) ...@@ -196,7 +171,6 @@ ExecInitAppend(Append *node, EState *estate, Plan *parent)
appendplans = node->appendplans; appendplans = node->appendplans;
nplans = length(appendplans); nplans = length(appendplans);
inheritrtable = node->inheritrtable;
initialized = (bool *) palloc(nplans * sizeof(bool)); initialized = (bool *) palloc(nplans * sizeof(bool));
MemSet(initialized, 0, nplans * sizeof(bool)); MemSet(initialized, 0, nplans * sizeof(bool));
...@@ -228,119 +202,25 @@ ExecInitAppend(Append *node, EState *estate, Plan *parent) ...@@ -228,119 +202,25 @@ ExecInitAppend(Append *node, EState *estate, Plan *parent)
*/ */
ExecInitResultTupleSlot(estate, &appendstate->cstate); ExecInitResultTupleSlot(estate, &appendstate->cstate);
/*
* If the inherits rtentry is the result relation, we have to make a
* result relation info list for all inheritors so we can update their
* indices and put the result tuples in the right place etc.
*
* e.g. replace p (age = p.age + 1) from p in person*
*/
if ((es_rri != (RelationInfo *) NULL) &&
(node->inheritrelid == es_rri->ri_RangeTableIndex))
{
List *resultList = NIL;
Oid initial_reloid = RelationGetRelid(es_rri->ri_RelationDesc);
List *rtentryP;
inherited_result_rel = true;
foreach(rtentryP, inheritrtable)
{
RangeTblEntry *rtentry = lfirst(rtentryP);
Oid reloid = rtentry->relid;
RelationInfo *rri;
/*
* We must recycle the RelationInfo already opened by InitPlan()
* for the parent rel, else we will leak the associated relcache
* refcount.
*/
if (reloid == initial_reloid)
{
Assert(es_rri != NULL); /* check we didn't use it already */
rri = es_rri;
es_rri = NULL;
}
else
{
rri = makeNode(RelationInfo);
rri->ri_RangeTableIndex = node->inheritrelid;
rri->ri_RelationDesc = heap_open(reloid, RowExclusiveLock);
rri->ri_NumIndices = 0;
rri->ri_IndexRelationDescs = NULL; /* index descs */
rri->ri_IndexRelationInfo = NULL; /* index key info */
/*
* XXX if the operation is a DELETE then we need not open
* indices, but how to tell that here?
*/
if (rri->ri_RelationDesc->rd_rel->relhasindex)
ExecOpenIndices(rri);
}
/*
* NB: the as_result_relation_info_list must be in the same
* order as the rtentry list otherwise update or delete on
* inheritance hierarchies won't work.
*/
resultList = lappend(resultList, rri);
}
appendstate->as_result_relation_info_list = resultList;
/* Check that we recycled InitPlan()'s RelationInfo */
Assert(es_rri == NULL);
/* Just for paranoia's sake, clear link until we set it properly */
estate->es_result_relation_info = NULL;
}
/* ---------------- /* ----------------
* call ExecInitNode on each of the plans in our list * call ExecInitNode on each of the plans in our list
* and save the results into the array "initialized" * and save the results into the array "initialized"
* ---------------- * ----------------
*/ */
junkList = NIL;
for (i = 0; i < nplans; i++) for (i = 0; i < nplans; i++)
{ {
/* ----------------
* NOTE: we first modify range table in
* exec_append_initialize_next() and
* then initialize the subnode,
* since it may use the range table.
* ----------------
*/
appendstate->as_whichplan = i; appendstate->as_whichplan = i;
exec_append_initialize_next(node); exec_append_initialize_next(node);
initNode = (Plan *) nth(i, appendplans); initNode = (Plan *) nth(i, appendplans);
initialized[i] = ExecInitNode(initNode, estate, (Plan *) node); initialized[i] = ExecInitNode(initNode, estate, (Plan *) node);
/* ---------------
* Each targetlist in the subplan may need its own junk filter
*
* This is true only when the reln being replaced/deleted is
* the one that we're looking at the subclasses of
* ---------------
*/
if (inherited_result_rel)
{
JunkFilter *j = ExecInitJunkFilter(initNode->targetlist,
ExecGetTupType(initNode));
junkList = lappend(junkList, j);
}
} }
appendstate->as_junkFilter_list = junkList;
if (junkList != NIL)
estate->es_junkFilter = (JunkFilter *) lfirst(junkList);
/* ---------------- /* ----------------
* initialize the return type from the appropriate subplan. * initialize tuple type
* ---------------- * ----------------
*/ */
initNode = (Plan *) nth(0, appendplans); ExecAssignResultTypeFromTL((Plan *) node, &appendstate->cstate);
ExecAssignResultType(&appendstate->cstate, ExecGetTupType(initNode));
appendstate->cstate.cs_ProjInfo = NULL; appendstate->cstate.cs_ProjInfo = NULL;
/* ---------------- /* ----------------
...@@ -357,10 +237,9 @@ int ...@@ -357,10 +237,9 @@ int
ExecCountSlotsAppend(Append *node) ExecCountSlotsAppend(Append *node)
{ {
List *plan; List *plan;
List *appendplans = node->appendplans;
int nSlots = 0; int nSlots = 0;
foreach(plan, appendplans) foreach(plan, node->appendplans)
nSlots += ExecCountSlotsNode((Plan *) lfirst(plan)); nSlots += ExecCountSlotsNode((Plan *) lfirst(plan));
return nSlots + APPEND_NSLOTS; return nSlots + APPEND_NSLOTS;
} }
...@@ -430,16 +309,14 @@ ExecProcAppend(Append *node) ...@@ -430,16 +309,14 @@ ExecProcAppend(Append *node)
* direction and try processing again (recursively) * direction and try processing again (recursively)
* ---------------- * ----------------
*/ */
whichplan = appendstate->as_whichplan;
if (ScanDirectionIsForward(direction)) if (ScanDirectionIsForward(direction))
appendstate->as_whichplan = whichplan + 1; appendstate->as_whichplan++;
else else
appendstate->as_whichplan = whichplan - 1; appendstate->as_whichplan--;
/* ---------------- /* ----------------
* return something from next node or an empty slot * return something from next node or an empty slot
* all of our subplans have been exhausted. * if all of our subplans have been exhausted.
* ---------------- * ----------------
*/ */
if (exec_append_initialize_next(node)) if (exec_append_initialize_next(node))
...@@ -469,7 +346,6 @@ ExecEndAppend(Append *node) ...@@ -469,7 +346,6 @@ ExecEndAppend(Append *node)
List *appendplans; List *appendplans;
bool *initialized; bool *initialized;
int i; int i;
List *resultRelationInfoList;
/* ---------------- /* ----------------
* get information from the node * get information from the node
...@@ -490,40 +366,8 @@ ExecEndAppend(Append *node) ...@@ -490,40 +366,8 @@ ExecEndAppend(Append *node)
if (initialized[i]) if (initialized[i])
ExecEndNode((Plan *) nth(i, appendplans), (Plan *) node); ExecEndNode((Plan *) nth(i, appendplans), (Plan *) node);
} }
/* ----------------
* close out the different result relations
*
* NB: this must agree with what EndPlan() does to close a result rel
* ----------------
*/
resultRelationInfoList = appendstate->as_result_relation_info_list;
while (resultRelationInfoList != NIL)
{
RelationInfo *resultRelationInfo;
resultRelationInfo = (RelationInfo *) lfirst(resultRelationInfoList);
heap_close(resultRelationInfo->ri_RelationDesc, NoLock);
/* close indices on the result relation, too */
ExecCloseIndices(resultRelationInfo);
/*
* estate may (or may not) be pointing at one of my result relations.
* If so, make sure EndPlan() doesn't try to close it again!
*/
if (estate->es_result_relation_info == resultRelationInfo)
estate->es_result_relation_info = NULL;
pfree(resultRelationInfo);
resultRelationInfoList = lnext(resultRelationInfoList);
}
appendstate->as_result_relation_info_list = NIL;
/*
* XXX should free appendstate->as_junkfilter_list here
*/
} }
void void
ExecReScanAppend(Append *node, ExprContext *exprCtxt, Plan *parent) ExecReScanAppend(Append *node, ExprContext *exprCtxt, Plan *parent)
{ {
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/executor/nodeSeqscan.c,v 1.24 2000/07/12 02:37:04 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/executor/nodeSeqscan.c,v 1.25 2000/11/12 00:36:57 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -156,7 +156,6 @@ InitScanRelation(SeqScan *node, EState *estate, ...@@ -156,7 +156,6 @@ InitScanRelation(SeqScan *node, EState *estate,
ScanDirection direction; ScanDirection direction;
Relation currentRelation; Relation currentRelation;
HeapScanDesc currentScanDesc; HeapScanDesc currentScanDesc;
RelationInfo *resultRelationInfo;
/* ---------------- /* ----------------
* get the relation object id from the relid'th entry * get the relation object id from the relid'th entry
...@@ -169,7 +168,6 @@ InitScanRelation(SeqScan *node, EState *estate, ...@@ -169,7 +168,6 @@ InitScanRelation(SeqScan *node, EState *estate,
rtentry = rt_fetch(relid, rangeTable); rtentry = rt_fetch(relid, rangeTable);
reloid = rtentry->relid; reloid = rtentry->relid;
direction = estate->es_direction; direction = estate->es_direction;
resultRelationInfo = estate->es_result_relation_info;
ExecOpenScanR(reloid, /* relation */ ExecOpenScanR(reloid, /* relation */
0, /* nkeys */ 0, /* nkeys */
......
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.130 2000/11/05 22:50:19 vadim Exp $ * $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.131 2000/11/12 00:36:57 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -176,8 +176,7 @@ _copyAppend(Append *from) ...@@ -176,8 +176,7 @@ _copyAppend(Append *from)
* ---------------- * ----------------
*/ */
Node_Copy(from, newnode, appendplans); Node_Copy(from, newnode, appendplans);
newnode->inheritrelid = from->inheritrelid; newnode->isTarget = from->isTarget;
Node_Copy(from, newnode, inheritrtable);
return newnode; return newnode;
} }
...@@ -1275,6 +1274,30 @@ _copyTidPath(TidPath *from) ...@@ -1275,6 +1274,30 @@ _copyTidPath(TidPath *from)
return newnode; return newnode;
} }
/* ----------------
* _copyAppendPath
* ----------------
*/
static AppendPath *
_copyAppendPath(AppendPath *from)
{
AppendPath *newnode = makeNode(AppendPath);
/* ----------------
* copy the node superclass fields
* ----------------
*/
CopyPathFields((Path *) from, (Path *) newnode);
/* ----------------
* copy remainder of node
* ----------------
*/
Node_Copy(from, newnode, subpaths);
return newnode;
}
/* ---------------- /* ----------------
* CopyJoinPathFields * CopyJoinPathFields
* *
...@@ -1767,6 +1790,8 @@ _copyQuery(Query *from) ...@@ -1767,6 +1790,8 @@ _copyQuery(Query *from)
Node_Copy(from, newnode, setOperations); Node_Copy(from, newnode, setOperations);
newnode->resultRelations = listCopy(from->resultRelations);
/* /*
* We do not copy the planner internal fields: base_rel_list, * We do not copy the planner internal fields: base_rel_list,
* join_rel_list, equi_key_list, query_pathkeys. Not entirely clear if * join_rel_list, equi_key_list, query_pathkeys. Not entirely clear if
...@@ -2677,6 +2702,9 @@ copyObject(void *from) ...@@ -2677,6 +2702,9 @@ copyObject(void *from)
case T_TidPath: case T_TidPath:
retval = _copyTidPath(from); retval = _copyTidPath(from);
break; break;
case T_AppendPath:
retval = _copyAppendPath(from);
break;
case T_NestPath: case T_NestPath:
retval = _copyNestPath(from); retval = _copyNestPath(from);
break; break;
......
...@@ -20,7 +20,7 @@ ...@@ -20,7 +20,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.80 2000/11/05 22:50:19 vadim Exp $ * $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.81 2000/11/12 00:36:57 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -435,6 +435,16 @@ _equalTidPath(TidPath *a, TidPath *b) ...@@ -435,6 +435,16 @@ _equalTidPath(TidPath *a, TidPath *b)
return true; return true;
} }
static bool
_equalAppendPath(AppendPath *a, AppendPath *b)
{
if (!_equalPath((Path *) a, (Path *) b))
return false;
if (!equal(a->subpaths, b->subpaths))
return false;
return true;
}
static bool static bool
_equalJoinPath(JoinPath *a, JoinPath *b) _equalJoinPath(JoinPath *a, JoinPath *b)
{ {
...@@ -555,28 +565,6 @@ _equalStream(Stream *a, Stream *b) ...@@ -555,28 +565,6 @@ _equalStream(Stream *a, Stream *b)
return equal(a->downstream, b->downstream); return equal(a->downstream, b->downstream);
} }
/*
* Stuff from execnodes.h
*/
/*
* EState is a subclass of Node.
*/
static bool
_equalEState(EState *a, EState *b)
{
if (a->es_direction != b->es_direction)
return false;
if (!equal(a->es_range_table, b->es_range_table))
return false;
if (a->es_result_relation_info != b->es_result_relation_info)
return false;
return true;
}
/* /*
* Stuff from parsenodes.h * Stuff from parsenodes.h
*/ */
...@@ -624,6 +612,8 @@ _equalQuery(Query *a, Query *b) ...@@ -624,6 +612,8 @@ _equalQuery(Query *a, Query *b)
return false; return false;
if (!equal(a->setOperations, b->setOperations)) if (!equal(a->setOperations, b->setOperations))
return false; return false;
if (!equali(a->resultRelations, b->resultRelations))
return false;
/* /*
* We do not check the internal-to-the-planner fields: base_rel_list, * We do not check the internal-to-the-planner fields: base_rel_list,
...@@ -1851,14 +1841,13 @@ equal(void *a, void *b) ...@@ -1851,14 +1841,13 @@ equal(void *a, void *b)
case T_TidPath: case T_TidPath:
retval = _equalTidPath(a, b); retval = _equalTidPath(a, b);
break; break;
case T_AppendPath:
retval = _equalAppendPath(a, b);
break;
case T_IndexOptInfo: case T_IndexOptInfo:
retval = _equalIndexOptInfo(a, b); retval = _equalIndexOptInfo(a, b);
break; break;
case T_EState:
retval = _equalEState(a, b);
break;
case T_List: case T_List:
{ {
List *la = (List *) a; List *la = (List *) a;
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.131 2000/10/31 13:59:52 petere Exp $ * $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.132 2000/11/12 00:36:57 tgl Exp $
* *
* NOTES * NOTES
* Every (plan) node in POSTGRES has an associated "out" routine which * Every (plan) node in POSTGRES has an associated "out" routine which
...@@ -296,6 +296,9 @@ _outQuery(StringInfo str, Query *node) ...@@ -296,6 +296,9 @@ _outQuery(StringInfo str, Query *node)
appendStringInfo(str, " :setOperations "); appendStringInfo(str, " :setOperations ");
_outNode(str, node->setOperations); _outNode(str, node->setOperations);
appendStringInfo(str, " :resultRelations ");
_outIntList(str, node->resultRelations);
} }
static void static void
...@@ -327,17 +330,18 @@ _outSetOperationStmt(StringInfo str, SetOperationStmt *node) ...@@ -327,17 +330,18 @@ _outSetOperationStmt(StringInfo str, SetOperationStmt *node)
/* /*
* print the basic stuff of all nodes that inherit from Plan * print the basic stuff of all nodes that inherit from Plan
*
* NOTE: we deliberately omit the execution state (EState)
*/ */
static void static void
_outPlanInfo(StringInfo str, Plan *node) _outPlanInfo(StringInfo str, Plan *node)
{ {
appendStringInfo(str, appendStringInfo(str,
":startup_cost %.2f :total_cost %.2f :rows %.0f :width %d :state %s :qptargetlist ", ":startup_cost %.2f :total_cost %.2f :rows %.0f :width %d :qptargetlist ",
node->startup_cost, node->startup_cost,
node->total_cost, node->total_cost,
node->plan_rows, node->plan_rows,
node->plan_width, node->plan_width);
node->state ? "not-NULL" : "<>");
_outNode(str, node->targetlist); _outNode(str, node->targetlist);
appendStringInfo(str, " :qpqual "); appendStringInfo(str, " :qpqual ");
...@@ -394,9 +398,8 @@ _outAppend(StringInfo str, Append *node) ...@@ -394,9 +398,8 @@ _outAppend(StringInfo str, Append *node)
appendStringInfo(str, " :appendplans "); appendStringInfo(str, " :appendplans ");
_outNode(str, node->appendplans); _outNode(str, node->appendplans);
appendStringInfo(str, " :inheritrelid %u :inheritrtable ", appendStringInfo(str, " :isTarget %s ",
node->inheritrelid); node->isTarget ? "true" : "false");
_outNode(str, node->inheritrtable);
} }
/* /*
...@@ -945,25 +948,6 @@ _outJoinExpr(StringInfo str, JoinExpr *node) ...@@ -945,25 +948,6 @@ _outJoinExpr(StringInfo str, JoinExpr *node)
_outNode(str, node->colvars); _outNode(str, node->colvars);
} }
/*
* Stuff from execnodes.h
*/
/*
* EState is a subclass of Node.
*/
static void
_outEState(StringInfo str, EState *node)
{
appendStringInfo(str,
" ESTATE :direction %d :range_table ",
node->es_direction);
_outNode(str, node->es_range_table);
appendStringInfo(str, " :result_relation_info @ 0x%x ",
(int) (node->es_result_relation_info));
}
/* /*
* Stuff from relation.h * Stuff from relation.h
*/ */
...@@ -1107,10 +1091,27 @@ _outTidPath(StringInfo str, TidPath *node) ...@@ -1107,10 +1091,27 @@ _outTidPath(StringInfo str, TidPath *node)
appendStringInfo(str, " :tideval "); appendStringInfo(str, " :tideval ");
_outNode(str, node->tideval); _outNode(str, node->tideval);
appendStringInfo(str, " :un joined_relids "); appendStringInfo(str, " :unjoined_relids ");
_outIntList(str, node->unjoined_relids); _outIntList(str, node->unjoined_relids);
} }
/*
* AppendPath is a subclass of Path.
*/
static void
_outAppendPath(StringInfo str, AppendPath *node)
{
appendStringInfo(str,
" APPENDPATH :pathtype %d :startup_cost %.2f :total_cost %.2f :pathkeys ",
node->path.pathtype,
node->path.startup_cost,
node->path.total_cost);
_outNode(str, node->path.pathkeys);
appendStringInfo(str, " :subpaths ");
_outNode(str, node->subpaths);
}
/* /*
* NestPath is a subclass of Path * NestPath is a subclass of Path
*/ */
...@@ -1632,9 +1633,6 @@ _outNode(StringInfo str, void *obj) ...@@ -1632,9 +1633,6 @@ _outNode(StringInfo str, void *obj)
case T_JoinExpr: case T_JoinExpr:
_outJoinExpr(str, obj); _outJoinExpr(str, obj);
break; break;
case T_EState:
_outEState(str, obj);
break;
case T_RelOptInfo: case T_RelOptInfo:
_outRelOptInfo(str, obj); _outRelOptInfo(str, obj);
break; break;
...@@ -1656,6 +1654,9 @@ _outNode(StringInfo str, void *obj) ...@@ -1656,6 +1654,9 @@ _outNode(StringInfo str, void *obj)
case T_TidPath: case T_TidPath:
_outTidPath(str, obj); _outTidPath(str, obj);
break; break;
case T_AppendPath:
_outAppendPath(str, obj);
break;
case T_NestPath: case T_NestPath:
_outNestPath(str, obj); _outNestPath(str, obj);
break; break;
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.99 2000/10/22 22:14:54 petere Exp $ * $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.100 2000/11/12 00:36:57 tgl Exp $
* *
* NOTES * NOTES
* Most of the read functions for plan nodes are tested. (In fact, they * Most of the read functions for plan nodes are tested. (In fact, they
...@@ -150,6 +150,9 @@ _readQuery(void) ...@@ -150,6 +150,9 @@ _readQuery(void)
token = lsptok(NULL, &length); /* skip :setOperations */ token = lsptok(NULL, &length); /* skip :setOperations */
local_node->setOperations = nodeRead(true); local_node->setOperations = nodeRead(true);
token = lsptok(NULL, &length); /* skip :resultRelations */
local_node->resultRelations = toIntList(nodeRead(true));
return local_node; return local_node;
} }
...@@ -260,17 +263,6 @@ _getPlan(Plan *node) ...@@ -260,17 +263,6 @@ _getPlan(Plan *node)
token = lsptok(NULL, &length); /* get the plan_width */ token = lsptok(NULL, &length); /* get the plan_width */
node->plan_width = atoi(token); node->plan_width = atoi(token);
token = lsptok(NULL, &length); /* eat the :state stuff */
token = lsptok(NULL, &length); /* now get the state */
if (length == 0)
node->state = (EState *) NULL;
else
{ /* Disgusting hack until I figure out what
* to do here */
node->state = (EState *) !NULL;
}
token = lsptok(NULL, &length); /* eat :qptargetlist */ token = lsptok(NULL, &length); /* eat :qptargetlist */
node->targetlist = nodeRead(true); node->targetlist = nodeRead(true);
...@@ -283,6 +275,8 @@ _getPlan(Plan *node) ...@@ -283,6 +275,8 @@ _getPlan(Plan *node)
token = lsptok(NULL, &length); /* eat :righttree */ token = lsptok(NULL, &length); /* eat :righttree */
node->righttree = (Plan *) nodeRead(true); node->righttree = (Plan *) nodeRead(true);
node->state = (EState *) NULL; /* never read in */
return; return;
} }
...@@ -348,12 +342,9 @@ _readAppend(void) ...@@ -348,12 +342,9 @@ _readAppend(void)
token = lsptok(NULL, &length); /* eat :appendplans */ token = lsptok(NULL, &length); /* eat :appendplans */
local_node->appendplans = nodeRead(true); /* now read it */ local_node->appendplans = nodeRead(true); /* now read it */
token = lsptok(NULL, &length); /* eat :inheritrelid */ token = lsptok(NULL, &length); /* eat :isTarget */
token = lsptok(NULL, &length); /* get inheritrelid */ token = lsptok(NULL, &length); /* get isTarget */
local_node->inheritrelid = strtoul(token, NULL, 10); local_node->isTarget = (token[0] == 't') ? true : false;
token = lsptok(NULL, &length); /* eat :inheritrtable */
local_node->inheritrtable = nodeRead(true); /* now read it */
return local_node; return local_node;
} }
...@@ -1297,43 +1288,6 @@ _readJoinExpr(void) ...@@ -1297,43 +1288,6 @@ _readJoinExpr(void)
return local_node; return local_node;
} }
/*
* Stuff from execnodes.h
*/
/* ----------------
* _readEState
*
* EState is a subclass of Node.
* ----------------
*/
static EState *
_readEState(void)
{
EState *local_node;
char *token;
int length;
local_node = makeNode(EState);
token = lsptok(NULL, &length); /* get :direction */
token = lsptok(NULL, &length); /* now read it */
local_node->es_direction = atoi(token);
token = lsptok(NULL, &length); /* get :range_table */
local_node->es_range_table = nodeRead(true); /* now read it */
token = lsptok(NULL, &length); /* get :result_relation_info */
token = lsptok(NULL, &length); /* get @ */
token = lsptok(NULL, &length); /* now read it */
sscanf(token, "%x", (unsigned int *) &local_node->es_result_relation_info);
return local_node;
}
/* /*
* Stuff from relation.h * Stuff from relation.h
*/ */
...@@ -1639,6 +1593,42 @@ _readTidPath(void) ...@@ -1639,6 +1593,42 @@ _readTidPath(void)
return local_node; return local_node;
} }
/* ----------------
* _readAppendPath
*
* AppendPath is a subclass of Path.
* ----------------
*/
static AppendPath *
_readAppendPath(void)
{
AppendPath *local_node;
char *token;
int length;
local_node = makeNode(AppendPath);
token = lsptok(NULL, &length); /* get :pathtype */
token = lsptok(NULL, &length); /* now read it */
local_node->path.pathtype = atol(token);
token = lsptok(NULL, &length); /* get :startup_cost */
token = lsptok(NULL, &length); /* now read it */
local_node->path.startup_cost = (Cost) atof(token);
token = lsptok(NULL, &length); /* get :total_cost */
token = lsptok(NULL, &length); /* now read it */
local_node->path.total_cost = (Cost) atof(token);
token = lsptok(NULL, &length); /* get :pathkeys */
local_node->path.pathkeys = nodeRead(true); /* now read it */
token = lsptok(NULL, &length); /* get :subpaths */
local_node->subpaths = nodeRead(true); /* now read it */
return local_node;
}
/* ---------------- /* ----------------
* _readNestPath * _readNestPath
* *
...@@ -1984,8 +1974,6 @@ parsePlanString(void) ...@@ -1984,8 +1974,6 @@ parsePlanString(void)
return_value = _readOper(); return_value = _readOper();
else if (length == 5 && strncmp(token, "PARAM", length) == 0) else if (length == 5 && strncmp(token, "PARAM", length) == 0)
return_value = _readParam(); return_value = _readParam();
else if (length == 6 && strncmp(token, "ESTATE", length) == 0)
return_value = _readEState();
else if (length == 10 && strncmp(token, "RELOPTINFO", length) == 0) else if (length == 10 && strncmp(token, "RELOPTINFO", length) == 0)
return_value = _readRelOptInfo(); return_value = _readRelOptInfo();
else if (length == 11 && strncmp(token, "TARGETENTRY", length) == 0) else if (length == 11 && strncmp(token, "TARGETENTRY", length) == 0)
...@@ -1998,6 +1986,8 @@ parsePlanString(void) ...@@ -1998,6 +1986,8 @@ parsePlanString(void)
return_value = _readIndexPath(); return_value = _readIndexPath();
else if (length == 7 && strncmp(token, "TIDPATH", length) == 0) else if (length == 7 && strncmp(token, "TIDPATH", length) == 0)
return_value = _readTidPath(); return_value = _readTidPath();
else if (length == 10 && strncmp(token, "APPENDPATH", length) == 0)
return_value = _readAppendPath();
else if (length == 8 && strncmp(token, "NESTPATH", length) == 0) else if (length == 8 && strncmp(token, "NESTPATH", length) == 0)
return_value = _readNestPath(); return_value = _readNestPath();
else if (length == 9 && strncmp(token, "MERGEPATH", length) == 0) else if (length == 9 && strncmp(token, "MERGEPATH", length) == 0)
......
...@@ -4,11 +4,11 @@ Summary ...@@ -4,11 +4,11 @@ Summary
These directories take the Query structure returned by the parser, and These directories take the Query structure returned by the parser, and
generate a plan used by the executor. The /plan directory generates the generate a plan used by the executor. The /plan directory generates the
actual output plan, the /path code generates all possible ways to join the actual output plan, the /path code generates all possible ways to join the
tables, and /prep handles special cases like inheritance. /util is utility tables, and /prep handles various preprocessing steps for special cases.
stuff. /geqo is the separate "genetic optimization" planner --- it does /util is utility stuff. /geqo is the separate "genetic optimization" planner
a semi-random search through the join tree space, rather than exhaustively --- it does a semi-random search through the join tree space, rather than
considering all possible join trees. (But each join considered by /geqo exhaustively considering all possible join trees. (But each join considered
is given to /path to create paths for, so we consider all possible by /geqo is given to /path to create paths for, so we consider all possible
implementation paths for each specific join pair even in GEQO mode.) implementation paths for each specific join pair even in GEQO mode.)
...@@ -210,10 +210,10 @@ planner() ...@@ -210,10 +210,10 @@ planner()
thereby reducing the accuracy of selectivity estimates. thereby reducing the accuracy of selectivity estimates.
process sublinks process sublinks
convert Vars of outer query levels into Params convert Vars of outer query levels into Params
--union_planner() --grouping_planner()
handle unions and inheritance by mutual recursion with prepunion.c routines preprocess target list for non-SELECT queries
preprocess target list handle UNION/INTERSECT/EXCEPT, GROUP BY, HAVING, aggregates,
handle GROUP BY, HAVING, aggregates, ORDER BY, DISTINCT ORDER BY, DISTINCT, LIMIT
--query_planner() --query_planner()
pull out constant quals, which can be used to gate execution of the pull out constant quals, which can be used to gate execution of the
whole plan (if any are found, we make a top-level Result node whole plan (if any are found, we make a top-level Result node
...@@ -239,11 +239,12 @@ planner() ...@@ -239,11 +239,12 @@ planner()
Loop back if this wasn't the top join level. Loop back if this wasn't the top join level.
Back at query_planner: Back at query_planner:
put back constant quals and non-simplified target list put back constant quals and non-simplified target list
Back at union_planner: Back at grouping_planner:
do grouping(GROUP) do grouping(GROUP)
do aggregates do aggregates
make unique(DISTINCT) make unique(DISTINCT)
make sort(ORDER BY) make sort(ORDER BY)
make limit(LIMIT/OFFSET)
Optimizer Data Structures Optimizer Data Structures
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/allpaths.c,v 1.66 2000/10/05 19:11:28 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/optimizer/path/allpaths.c,v 1.67 2000/11/12 00:36:58 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
#include "optimizer/paths.h" #include "optimizer/paths.h"
#include "optimizer/plancat.h" #include "optimizer/plancat.h"
#include "optimizer/planner.h" #include "optimizer/planner.h"
#include "optimizer/prep.h"
#include "parser/parsetree.h" #include "parser/parsetree.h"
...@@ -28,7 +29,12 @@ bool enable_geqo = true; ...@@ -28,7 +29,12 @@ bool enable_geqo = true;
int geqo_rels = DEFAULT_GEQO_RELS; int geqo_rels = DEFAULT_GEQO_RELS;
static void set_base_rel_pathlist(Query *root); static void set_base_rel_pathlists(Query *root);
static void set_plain_rel_pathlist(Query *root, RelOptInfo *rel,
RangeTblEntry *rte);
static void set_inherited_rel_pathlist(Query *root, RelOptInfo *rel,
RangeTblEntry *rte,
List *inheritlist);
static RelOptInfo *make_one_rel_by_joins(Query *root, int levels_needed, static RelOptInfo *make_one_rel_by_joins(Query *root, int levels_needed,
List *initial_rels); List *initial_rels);
...@@ -51,7 +57,7 @@ make_one_rel(Query *root) ...@@ -51,7 +57,7 @@ make_one_rel(Query *root)
/* /*
* Generate access paths for the base rels. * Generate access paths for the base rels.
*/ */
set_base_rel_pathlist(root); set_base_rel_pathlists(root);
/* /*
* Generate access paths for the entire join tree. * Generate access paths for the entire join tree.
...@@ -69,23 +75,26 @@ make_one_rel(Query *root) ...@@ -69,23 +75,26 @@ make_one_rel(Query *root)
} }
/* /*
* set_base_rel_pathlist * set_base_rel_pathlists
* Finds all paths available for scanning each base-relation entry. * Finds all paths available for scanning each base-relation entry.
* Sequential scan and any available indices are considered. * Sequential scan and any available indices are considered.
* Each useful path is attached to its relation's 'pathlist' field. * Each useful path is attached to its relation's 'pathlist' field.
*/ */
static void static void
set_base_rel_pathlist(Query *root) set_base_rel_pathlists(Query *root)
{ {
List *rellist; List *rellist;
foreach(rellist, root->base_rel_list) foreach(rellist, root->base_rel_list)
{ {
RelOptInfo *rel = (RelOptInfo *) lfirst(rellist); RelOptInfo *rel = (RelOptInfo *) lfirst(rellist);
Index rti;
RangeTblEntry *rte; RangeTblEntry *rte;
List *inheritlist;
Assert(length(rel->relids) == 1); /* better be base rel */ Assert(length(rel->relids) == 1); /* better be base rel */
rte = rt_fetch(lfirsti(rel->relids), root->rtable); rti = lfirsti(rel->relids);
rte = rt_fetch(rti, root->rtable);
if (rel->issubquery) if (rel->issubquery)
{ {
...@@ -109,47 +118,163 @@ set_base_rel_pathlist(Query *root) ...@@ -109,47 +118,163 @@ set_base_rel_pathlist(Query *root)
/* Generate appropriate path */ /* Generate appropriate path */
add_path(rel, create_subqueryscan_path(rel)); add_path(rel, create_subqueryscan_path(rel));
/* Select cheapest path (pretty easy in this case...) */
set_cheapest(rel);
}
else if ((inheritlist = expand_inherted_rtentry(root, rti)) != NIL)
{
/* Relation is root of an inheritance tree, process specially */
set_inherited_rel_pathlist(root, rel, rte, inheritlist);
} }
else else
{ {
/* Plain relation */ /* Plain relation */
List *indices = find_secondary_indexes(rte->relid); set_plain_rel_pathlist(root, rel, rte);
}
}
}
/* Mark rel with estimated output rows, width, etc */ /*
set_baserel_size_estimates(root, rel); * set_plain_rel_pathlist
* Build access paths for a plain relation (no subquery, no inheritance)
*/
static void
set_plain_rel_pathlist(Query *root, RelOptInfo *rel, RangeTblEntry *rte)
{
List *indices = find_secondary_indexes(rte->relid);
/* /* Mark rel with estimated output rows, width, etc */
* Generate paths and add them to the rel's pathlist. set_baserel_size_estimates(root, rel);
*
* Note: add_path() will discard any paths that are dominated by
* another available path, keeping only those paths that are
* superior along at least one dimension of cost or sortedness.
*/
/* Consider sequential scan */ /*
add_path(rel, create_seqscan_path(rel)); * Generate paths and add them to the rel's pathlist.
*
* Note: add_path() will discard any paths that are dominated by
* another available path, keeping only those paths that are
* superior along at least one dimension of cost or sortedness.
*/
/* Consider TID scans */ /* Consider sequential scan */
create_tidscan_paths(root, rel); add_path(rel, create_seqscan_path(rel));
/* Consider index paths for both simple and OR index clauses */ /* Consider TID scans */
create_index_paths(root, rel, indices, create_tidscan_paths(root, rel);
rel->baserestrictinfo,
rel->joininfo);
/* /* Consider index paths for both simple and OR index clauses */
* Note: create_or_index_paths depends on create_index_paths to create_index_paths(root, rel, indices,
* have marked OR restriction clauses with relevant indices; this rel->baserestrictinfo,
* is why it doesn't need to be given the list of indices. rel->joininfo);
*/
create_or_index_paths(root, rel, rel->baserestrictinfo); /*
} * Note: create_or_index_paths depends on create_index_paths to
* have marked OR restriction clauses with relevant indices; this
* is why it doesn't need to be given the list of indices.
*/
create_or_index_paths(root, rel, rel->baserestrictinfo);
/* Now find the cheapest of the paths for this rel */
set_cheapest(rel);
}
/*
* set_inherited_rel_pathlist
* Build access paths for a inheritance tree rooted at rel
*
* inheritlist is a list of RT indexes of all tables in the inheritance tree,
* including the parent itself. Note we will not come here unless there's
* at least one child in addition to the parent.
*/
static void
set_inherited_rel_pathlist(Query *root, RelOptInfo *rel, RangeTblEntry *rte,
List *inheritlist)
{
int parentRTindex = lfirsti(rel->relids);
Oid parentOID = rte->relid;
List *subpaths = NIL;
List *il;
/*
* XXX for now, can't handle inherited expansion of FOR UPDATE;
* can we do better?
*/
if (intMember(parentRTindex, root->rowMarks))
elog(ERROR, "SELECT FOR UPDATE is not supported for inherit queries");
/* Now find the cheapest of the paths for this rel */ /*
set_cheapest(rel); * Recompute size estimates for whole inheritance tree
*/
rel->rows = 0;
rel->width = 0;
/*
* Generate access paths for each table in the tree (parent AND children),
* and pick the cheapest path for each table.
*/
foreach(il, inheritlist)
{
int childRTindex = lfirsti(il);
RangeTblEntry *childrte;
Oid childOID;
RelOptInfo *childrel;
childrte = rt_fetch(childRTindex, root->rtable);
childOID = childrte->relid;
/*
* Make a RelOptInfo for the child so we can do planning. Do NOT
* attach the RelOptInfo to the query's base_rel_list, however.
*
* NOTE: when childRTindex == parentRTindex, we create a second
* RelOptInfo for the same relation. This RelOptInfo will represent
* the parent table alone, whereas the original RelOptInfo represents
* the union of the inheritance tree members.
*/
childrel = make_base_rel(root, childRTindex);
/*
* Copy the parent's targetlist and restriction quals to the child,
* with attribute-number adjustment if needed. We don't bother
* to copy the join quals, since we can't do any joining here.
*/
childrel->targetlist = (List *)
adjust_inherited_attrs((Node *) rel->targetlist,
parentRTindex,
parentOID,
childRTindex,
childOID);
childrel->baserestrictinfo = (List *)
adjust_inherited_attrs((Node *) rel->baserestrictinfo,
parentRTindex,
parentOID,
childRTindex,
childOID);
childrel->baserestrictcost = rel->baserestrictcost;
/*
* Now compute child access paths, and save the cheapest.
*/
set_plain_rel_pathlist(root, childrel, childrte);
subpaths = lappend(subpaths, childrel->cheapest_total_path);
/* Also update total size estimates */
rel->rows += childrel->rows;
if (childrel->width > rel->width)
rel->width = childrel->width;
} }
/*
* Finally, build Append path and install it as the only access
* path for the parent rel.
*/
add_path(rel, (Path *) create_append_path(rel, subpaths));
/* Select cheapest path (pretty easy in this case...) */
set_cheapest(rel);
} }
/* /*
* make_fromexpr_rel * make_fromexpr_rel
* Build access paths for a FromExpr jointree node. * Build access paths for a FromExpr jointree node.
......
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/pathkeys.c,v 1.26 2000/09/29 18:21:32 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/optimizer/path/pathkeys.c,v 1.27 2000/11/12 00:36:58 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -566,8 +566,8 @@ build_join_pathkeys(List *outer_pathkeys, ...@@ -566,8 +566,8 @@ build_join_pathkeys(List *outer_pathkeys,
* NB: the result is NOT in canonical form, but must be passed through * NB: the result is NOT in canonical form, but must be passed through
* canonicalize_pathkeys() before it can be used for comparisons or * canonicalize_pathkeys() before it can be used for comparisons or
* labeling relation sort orders. (We do things this way because * labeling relation sort orders. (We do things this way because
* union_planner needs to be able to construct requested pathkeys before * grouping_planner needs to be able to construct requested pathkeys
* the pathkey equivalence sets have been created for the query.) * before the pathkey equivalence sets have been created for the query.)
* *
* 'sortclauses' is a list of SortClause or GroupClause nodes * 'sortclauses' is a list of SortClause or GroupClause nodes
* 'tlist' is the targetlist to find the referenced tlist entries in * 'tlist' is the targetlist to find the referenced tlist entries in
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.99 2000/10/26 21:36:09 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.100 2000/11/12 00:36:58 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -32,35 +32,38 @@ ...@@ -32,35 +32,38 @@
#include "utils/syscache.h" #include "utils/syscache.h"
static List *switch_outer(List *clauses); static Scan *create_scan_plan(Query *root, Path *best_path);
static Scan *create_scan_node(Query *root, Path *best_path, List *tlist); static Join *create_join_plan(Query *root, JoinPath *best_path);
static Join *create_join_node(Query *root, JoinPath *best_path, List *tlist); static Append *create_append_plan(Query *root, AppendPath *best_path);
static SeqScan *create_seqscan_node(Path *best_path, List *tlist, static SeqScan *create_seqscan_plan(Path *best_path, List *tlist,
List *scan_clauses); List *scan_clauses);
static IndexScan *create_indexscan_node(Query *root, IndexPath *best_path, static IndexScan *create_indexscan_plan(Query *root, IndexPath *best_path,
List *tlist, List *scan_clauses); List *tlist, List *scan_clauses);
static TidScan *create_tidscan_node(TidPath *best_path, List *tlist, static TidScan *create_tidscan_plan(TidPath *best_path, List *tlist,
List *scan_clauses); List *scan_clauses);
static SubqueryScan *create_subqueryscan_node(Path *best_path, static SubqueryScan *create_subqueryscan_plan(Path *best_path,
List *tlist, List *scan_clauses); List *tlist, List *scan_clauses);
static NestLoop *create_nestloop_node(NestPath *best_path, List *tlist, static NestLoop *create_nestloop_plan(NestPath *best_path, List *tlist,
List *joinclauses, List *otherclauses, List *joinclauses, List *otherclauses,
Plan *outer_node, List *outer_tlist, Plan *outer_plan, List *outer_tlist,
Plan *inner_node, List *inner_tlist); Plan *inner_plan, List *inner_tlist);
static MergeJoin *create_mergejoin_node(MergePath *best_path, List *tlist, static MergeJoin *create_mergejoin_plan(MergePath *best_path, List *tlist,
List *joinclauses, List *otherclauses, List *joinclauses, List *otherclauses,
Plan *outer_node, List *outer_tlist, Plan *outer_plan, List *outer_tlist,
Plan *inner_node, List *inner_tlist); Plan *inner_plan, List *inner_tlist);
static HashJoin *create_hashjoin_node(HashPath *best_path, List *tlist, static HashJoin *create_hashjoin_plan(HashPath *best_path, List *tlist,
List *joinclauses, List *otherclauses, List *joinclauses, List *otherclauses,
Plan *outer_node, List *outer_tlist, Plan *outer_plan, List *outer_tlist,
Plan *inner_node, List *inner_tlist); Plan *inner_plan, List *inner_tlist);
static List *fix_indxqual_references(List *indexquals, IndexPath *index_path); static List *fix_indxqual_references(List *indexquals, IndexPath *index_path);
static List *fix_indxqual_sublist(List *indexqual, int baserelid, Oid relam, static List *fix_indxqual_sublist(List *indexqual, int baserelid, Oid relam,
Form_pg_index index); Form_pg_index index);
static Node *fix_indxqual_operand(Node *node, int baserelid, static Node *fix_indxqual_operand(Node *node, int baserelid,
Form_pg_index index, Form_pg_index index,
Oid *opclass); Oid *opclass);
static List *switch_outer(List *clauses);
static void copy_path_costsize(Plan *dest, Path *src);
static void copy_plan_costsize(Plan *dest, Plan *src);
static SeqScan *make_seqscan(List *qptlist, List *qpqual, Index scanrelid); static SeqScan *make_seqscan(List *qptlist, List *qpqual, Index scanrelid);
static IndexScan *make_indexscan(List *qptlist, List *qpqual, Index scanrelid, static IndexScan *make_indexscan(List *qptlist, List *qpqual, Index scanrelid,
List *indxid, List *indxqual, List *indxid, List *indxqual,
...@@ -83,7 +86,6 @@ static MergeJoin *make_mergejoin(List *tlist, ...@@ -83,7 +86,6 @@ static MergeJoin *make_mergejoin(List *tlist,
List *mergeclauses, List *mergeclauses,
Plan *lefttree, Plan *righttree, Plan *lefttree, Plan *righttree,
JoinType jointype); JoinType jointype);
static void copy_path_costsize(Plan *dest, Path *src);
/* /*
* create_plan * create_plan
...@@ -98,13 +100,12 @@ static void copy_path_costsize(Plan *dest, Path *src); ...@@ -98,13 +100,12 @@ static void copy_path_costsize(Plan *dest, Path *src);
* *
* best_path is the best access path * best_path is the best access path
* *
* Returns the access plan. * Returns a Plan tree.
*/ */
Plan * Plan *
create_plan(Query *root, Path *best_path) create_plan(Query *root, Path *best_path)
{ {
List *tlist = best_path->parent->targetlist; Plan *plan;
Plan *plan_node = (Plan *) NULL;
switch (best_path->pathtype) switch (best_path->pathtype)
{ {
...@@ -112,18 +113,22 @@ create_plan(Query *root, Path *best_path) ...@@ -112,18 +113,22 @@ create_plan(Query *root, Path *best_path)
case T_SeqScan: case T_SeqScan:
case T_TidScan: case T_TidScan:
case T_SubqueryScan: case T_SubqueryScan:
plan_node = (Plan *) create_scan_node(root, best_path, tlist); plan = (Plan *) create_scan_plan(root, best_path);
break; break;
case T_HashJoin: case T_HashJoin:
case T_MergeJoin: case T_MergeJoin:
case T_NestLoop: case T_NestLoop:
plan_node = (Plan *) create_join_node(root, plan = (Plan *) create_join_plan(root,
(JoinPath *) best_path, (JoinPath *) best_path);
tlist); break;
case T_Append:
plan = (Plan *) create_append_plan(root,
(AppendPath *) best_path);
break; break;
default: default:
elog(ERROR, "create_plan: unknown pathtype %d", elog(ERROR, "create_plan: unknown pathtype %d",
best_path->pathtype); best_path->pathtype);
plan = NULL; /* keep compiler quiet */
break; break;
} }
...@@ -131,30 +136,29 @@ create_plan(Query *root, Path *best_path) ...@@ -131,30 +136,29 @@ create_plan(Query *root, Path *best_path)
/* sort clauses by cost/(1-selectivity) -- JMH 2/26/92 */ /* sort clauses by cost/(1-selectivity) -- JMH 2/26/92 */
if (XfuncMode != XFUNC_OFF) if (XfuncMode != XFUNC_OFF)
{ {
set_qpqual((Plan) plan_node, set_qpqual((Plan) plan,
lisp_qsort(get_qpqual((Plan) plan_node), lisp_qsort(get_qpqual((Plan) plan),
xfunc_clause_compare)); xfunc_clause_compare));
if (XfuncMode != XFUNC_NOR) if (XfuncMode != XFUNC_NOR)
/* sort the disjuncts within each clause by cost -- JMH 3/4/92 */ /* sort the disjuncts within each clause by cost -- JMH 3/4/92 */
xfunc_disjunct_sort(plan_node->qpqual); xfunc_disjunct_sort(plan->qpqual);
} }
#endif #endif
return plan_node; return plan;
} }
/* /*
* create_scan_node * create_scan_plan
* Create a scan path for the parent relation of 'best_path'. * Create a scan plan for the parent relation of 'best_path'.
* *
* tlist is the targetlist for the base relation scanned by 'best_path' * Returns a Plan node.
*
* Returns the scan node.
*/ */
static Scan * static Scan *
create_scan_node(Query *root, Path *best_path, List *tlist) create_scan_plan(Query *root, Path *best_path)
{ {
Scan *node = NULL; Scan *plan;
List *tlist = best_path->parent->targetlist;
List *scan_clauses; List *scan_clauses;
/* /*
...@@ -166,65 +170,64 @@ create_scan_node(Query *root, Path *best_path, List *tlist) ...@@ -166,65 +170,64 @@ create_scan_node(Query *root, Path *best_path, List *tlist)
switch (best_path->pathtype) switch (best_path->pathtype)
{ {
case T_SeqScan: case T_SeqScan:
node = (Scan *) create_seqscan_node(best_path, plan = (Scan *) create_seqscan_plan(best_path,
tlist, tlist,
scan_clauses); scan_clauses);
break; break;
case T_IndexScan: case T_IndexScan:
node = (Scan *) create_indexscan_node(root, plan = (Scan *) create_indexscan_plan(root,
(IndexPath *) best_path, (IndexPath *) best_path,
tlist, tlist,
scan_clauses); scan_clauses);
break; break;
case T_TidScan: case T_TidScan:
node = (Scan *) create_tidscan_node((TidPath *) best_path, plan = (Scan *) create_tidscan_plan((TidPath *) best_path,
tlist, tlist,
scan_clauses); scan_clauses);
break; break;
case T_SubqueryScan: case T_SubqueryScan:
node = (Scan *) create_subqueryscan_node(best_path, plan = (Scan *) create_subqueryscan_plan(best_path,
tlist, tlist,
scan_clauses); scan_clauses);
break; break;
default: default:
elog(ERROR, "create_scan_node: unknown node type: %d", elog(ERROR, "create_scan_plan: unknown node type: %d",
best_path->pathtype); best_path->pathtype);
plan = NULL; /* keep compiler quiet */
break; break;
} }
return node; return plan;
} }
/* /*
* create_join_node * create_join_plan
* Create a join path for 'best_path' and(recursively) paths for its * Create a join plan for 'best_path' and (recursively) plans for its
* inner and outer paths. * inner and outer paths.
* *
* 'tlist' is the targetlist for the join relation corresponding to * Returns a Plan node.
* 'best_path'
*
* Returns the join node.
*/ */
static Join * static Join *
create_join_node(Query *root, JoinPath *best_path, List *tlist) create_join_plan(Query *root, JoinPath *best_path)
{ {
Plan *outer_node; List *join_tlist = best_path->path.parent->targetlist;
Plan *outer_plan;
List *outer_tlist; List *outer_tlist;
Plan *inner_node; Plan *inner_plan;
List *inner_tlist; List *inner_tlist;
List *joinclauses; List *joinclauses;
List *otherclauses; List *otherclauses;
Join *retval = NULL; Join *plan;
outer_node = create_plan(root, best_path->outerjoinpath); outer_plan = create_plan(root, best_path->outerjoinpath);
outer_tlist = outer_node->targetlist; outer_tlist = outer_plan->targetlist;
inner_node = create_plan(root, best_path->innerjoinpath); inner_plan = create_plan(root, best_path->innerjoinpath);
inner_tlist = inner_node->targetlist; inner_tlist = inner_plan->targetlist;
if (IS_OUTER_JOIN(best_path->jointype)) if (IS_OUTER_JOIN(best_path->jointype))
{ {
...@@ -241,38 +244,40 @@ create_join_node(Query *root, JoinPath *best_path, List *tlist) ...@@ -241,38 +244,40 @@ create_join_node(Query *root, JoinPath *best_path, List *tlist)
switch (best_path->path.pathtype) switch (best_path->path.pathtype)
{ {
case T_MergeJoin: case T_MergeJoin:
retval = (Join *) create_mergejoin_node((MergePath *) best_path, plan = (Join *) create_mergejoin_plan((MergePath *) best_path,
tlist, join_tlist,
joinclauses, joinclauses,
otherclauses, otherclauses,
outer_node, outer_plan,
outer_tlist, outer_tlist,
inner_node, inner_plan,
inner_tlist); inner_tlist);
break; break;
case T_HashJoin: case T_HashJoin:
retval = (Join *) create_hashjoin_node((HashPath *) best_path, plan = (Join *) create_hashjoin_plan((HashPath *) best_path,
tlist, join_tlist,
joinclauses, joinclauses,
otherclauses, otherclauses,
outer_node, outer_plan,
outer_tlist, outer_tlist,
inner_node, inner_plan,
inner_tlist); inner_tlist);
break; break;
case T_NestLoop: case T_NestLoop:
retval = (Join *) create_nestloop_node((NestPath *) best_path, plan = (Join *) create_nestloop_plan((NestPath *) best_path,
tlist, join_tlist,
joinclauses, joinclauses,
otherclauses, otherclauses,
outer_node, outer_plan,
outer_tlist, outer_tlist,
inner_node, inner_plan,
inner_tlist); inner_tlist);
break; break;
default: default:
elog(ERROR, "create_join_node: unknown node type: %d", elog(ERROR, "create_join_plan: unknown node type: %d",
best_path->path.pathtype); best_path->path.pathtype);
plan = NULL; /* keep compiler quiet */
break;
} }
#ifdef NOT_USED #ifdef NOT_USED
...@@ -283,14 +288,42 @@ create_join_node(Query *root, JoinPath *best_path, List *tlist) ...@@ -283,14 +288,42 @@ create_join_node(Query *root, JoinPath *best_path, List *tlist)
* JMH, 6/15/92 * JMH, 6/15/92
*/ */
if (get_loc_restrictinfo(best_path) != NIL) if (get_loc_restrictinfo(best_path) != NIL)
set_qpqual((Plan) retval, set_qpqual((Plan) plan,
nconc(get_qpqual((Plan) retval), nconc(get_qpqual((Plan) plan),
get_actual_clauses(get_loc_restrictinfo(best_path)))); get_actual_clauses(get_loc_restrictinfo(best_path))));
#endif #endif
return retval; return plan;
} }
/*
* create_append_plan
* Create an Append plan for 'best_path' and (recursively) plans
* for its subpaths.
*
* Returns a Plan node.
*/
static Append *
create_append_plan(Query *root, AppendPath *best_path)
{
Append *plan;
List *tlist = best_path->path.parent->targetlist;
List *subplans = NIL;
List *subpaths;
foreach(subpaths, best_path->subpaths)
{
Path *subpath = (Path *) lfirst(subpaths);
subplans = lappend(subplans, create_plan(root, subpath));
}
plan = make_append(subplans, false, tlist);
return plan;
}
/***************************************************************************** /*****************************************************************************
* *
* BASE-RELATION SCAN METHODS * BASE-RELATION SCAN METHODS
...@@ -299,14 +332,14 @@ create_join_node(Query *root, JoinPath *best_path, List *tlist) ...@@ -299,14 +332,14 @@ create_join_node(Query *root, JoinPath *best_path, List *tlist)
/* /*
* create_seqscan_node * create_seqscan_plan
* Returns a seqscan node for the base relation scanned by 'best_path' * Returns a seqscan plan for the base relation scanned by 'best_path'
* with restriction clauses 'scan_clauses' and targetlist 'tlist'. * with restriction clauses 'scan_clauses' and targetlist 'tlist'.
*/ */
static SeqScan * static SeqScan *
create_seqscan_node(Path *best_path, List *tlist, List *scan_clauses) create_seqscan_plan(Path *best_path, List *tlist, List *scan_clauses)
{ {
SeqScan *scan_node; SeqScan *scan_plan;
Index scan_relid; Index scan_relid;
/* there should be exactly one base rel involved... */ /* there should be exactly one base rel involved... */
...@@ -315,18 +348,18 @@ create_seqscan_node(Path *best_path, List *tlist, List *scan_clauses) ...@@ -315,18 +348,18 @@ create_seqscan_node(Path *best_path, List *tlist, List *scan_clauses)
scan_relid = (Index) lfirsti(best_path->parent->relids); scan_relid = (Index) lfirsti(best_path->parent->relids);
scan_node = make_seqscan(tlist, scan_plan = make_seqscan(tlist,
scan_clauses, scan_clauses,
scan_relid); scan_relid);
copy_path_costsize(&scan_node->plan, best_path); copy_path_costsize(&scan_plan->plan, best_path);
return scan_node; return scan_plan;
} }
/* /*
* create_indexscan_node * create_indexscan_plan
* Returns a indexscan node for the base relation scanned by 'best_path' * Returns a indexscan plan for the base relation scanned by 'best_path'
* with restriction clauses 'scan_clauses' and targetlist 'tlist'. * with restriction clauses 'scan_clauses' and targetlist 'tlist'.
* *
* The indexqual of the path contains a sublist of implicitly-ANDed qual * The indexqual of the path contains a sublist of implicitly-ANDed qual
...@@ -338,7 +371,7 @@ create_seqscan_node(Path *best_path, List *tlist, List *scan_clauses) ...@@ -338,7 +371,7 @@ create_seqscan_node(Path *best_path, List *tlist, List *scan_clauses)
* scan. * scan.
*/ */
static IndexScan * static IndexScan *
create_indexscan_node(Query *root, create_indexscan_plan(Query *root,
IndexPath *best_path, IndexPath *best_path,
List *tlist, List *tlist,
List *scan_clauses) List *scan_clauses)
...@@ -348,7 +381,7 @@ create_indexscan_node(Query *root, ...@@ -348,7 +381,7 @@ create_indexscan_node(Query *root,
List *qpqual; List *qpqual;
List *fixed_indxqual; List *fixed_indxqual;
List *ixid; List *ixid;
IndexScan *scan_node; IndexScan *scan_plan;
bool lossy = false; bool lossy = false;
/* there should be exactly one base rel involved... */ /* there should be exactly one base rel involved... */
...@@ -433,7 +466,7 @@ create_indexscan_node(Query *root, ...@@ -433,7 +466,7 @@ create_indexscan_node(Query *root,
*/ */
fixed_indxqual = fix_indxqual_references(indxqual, best_path); fixed_indxqual = fix_indxqual_references(indxqual, best_path);
scan_node = make_indexscan(tlist, scan_plan = make_indexscan(tlist,
qpqual, qpqual,
baserelid, baserelid,
best_path->indexid, best_path->indexid,
...@@ -441,22 +474,22 @@ create_indexscan_node(Query *root, ...@@ -441,22 +474,22 @@ create_indexscan_node(Query *root,
indxqual, indxqual,
best_path->indexscandir); best_path->indexscandir);
copy_path_costsize(&scan_node->scan.plan, &best_path->path); copy_path_costsize(&scan_plan->scan.plan, &best_path->path);
/* use the indexscan-specific rows estimate, not the parent rel's */ /* use the indexscan-specific rows estimate, not the parent rel's */
scan_node->scan.plan.plan_rows = best_path->rows; scan_plan->scan.plan.plan_rows = best_path->rows;
return scan_node; return scan_plan;
} }
/* /*
* create_tidscan_node * create_tidscan_plan
* Returns a tidscan node for the base relation scanned by 'best_path' * Returns a tidscan plan for the base relation scanned by 'best_path'
* with restriction clauses 'scan_clauses' and targetlist 'tlist'. * with restriction clauses 'scan_clauses' and targetlist 'tlist'.
*/ */
static TidScan * static TidScan *
create_tidscan_node(TidPath *best_path, List *tlist, List *scan_clauses) create_tidscan_plan(TidPath *best_path, List *tlist, List *scan_clauses)
{ {
TidScan *scan_node; TidScan *scan_plan;
Index scan_relid; Index scan_relid;
/* there should be exactly one base rel involved... */ /* there should be exactly one base rel involved... */
...@@ -465,28 +498,28 @@ create_tidscan_node(TidPath *best_path, List *tlist, List *scan_clauses) ...@@ -465,28 +498,28 @@ create_tidscan_node(TidPath *best_path, List *tlist, List *scan_clauses)
scan_relid = (Index) lfirsti(best_path->path.parent->relids); scan_relid = (Index) lfirsti(best_path->path.parent->relids);
scan_node = make_tidscan(tlist, scan_plan = make_tidscan(tlist,
scan_clauses, scan_clauses,
scan_relid, scan_relid,
best_path->tideval); best_path->tideval);
if (best_path->unjoined_relids) if (best_path->unjoined_relids)
scan_node->needRescan = true; scan_plan->needRescan = true;
copy_path_costsize(&scan_node->scan.plan, &best_path->path); copy_path_costsize(&scan_plan->scan.plan, &best_path->path);
return scan_node; return scan_plan;
} }
/* /*
* create_subqueryscan_node * create_subqueryscan_plan
* Returns a subqueryscan node for the base relation scanned by 'best_path' * Returns a subqueryscan plan for the base relation scanned by 'best_path'
* with restriction clauses 'scan_clauses' and targetlist 'tlist'. * with restriction clauses 'scan_clauses' and targetlist 'tlist'.
*/ */
static SubqueryScan * static SubqueryScan *
create_subqueryscan_node(Path *best_path, List *tlist, List *scan_clauses) create_subqueryscan_plan(Path *best_path, List *tlist, List *scan_clauses)
{ {
SubqueryScan *scan_node; SubqueryScan *scan_plan;
Index scan_relid; Index scan_relid;
/* there should be exactly one base rel involved... */ /* there should be exactly one base rel involved... */
...@@ -496,14 +529,12 @@ create_subqueryscan_node(Path *best_path, List *tlist, List *scan_clauses) ...@@ -496,14 +529,12 @@ create_subqueryscan_node(Path *best_path, List *tlist, List *scan_clauses)
scan_relid = (Index) lfirsti(best_path->parent->relids); scan_relid = (Index) lfirsti(best_path->parent->relids);
scan_node = make_subqueryscan(tlist, scan_plan = make_subqueryscan(tlist,
scan_clauses, scan_clauses,
scan_relid, scan_relid,
best_path->parent->subplan); best_path->parent->subplan);
copy_path_costsize(&scan_node->scan.plan, best_path); return scan_plan;
return scan_node;
} }
/***************************************************************************** /*****************************************************************************
...@@ -528,18 +559,18 @@ create_subqueryscan_node(Path *best_path, List *tlist, List *scan_clauses) ...@@ -528,18 +559,18 @@ create_subqueryscan_node(Path *best_path, List *tlist, List *scan_clauses)
*****************************************************************************/ *****************************************************************************/
static NestLoop * static NestLoop *
create_nestloop_node(NestPath *best_path, create_nestloop_plan(NestPath *best_path,
List *tlist, List *tlist,
List *joinclauses, List *joinclauses,
List *otherclauses, List *otherclauses,
Plan *outer_node, Plan *outer_plan,
List *outer_tlist, List *outer_tlist,
Plan *inner_node, Plan *inner_plan,
List *inner_tlist) List *inner_tlist)
{ {
NestLoop *join_node; NestLoop *join_plan;
if (IsA(inner_node, IndexScan)) if (IsA(inner_plan, IndexScan))
{ {
/* /*
...@@ -563,7 +594,7 @@ create_nestloop_node(NestPath *best_path, ...@@ -563,7 +594,7 @@ create_nestloop_node(NestPath *best_path,
* and therefore has not itself done join_references renumbering * and therefore has not itself done join_references renumbering
* of the vars in its quals. * of the vars in its quals.
*/ */
IndexScan *innerscan = (IndexScan *) inner_node; IndexScan *innerscan = (IndexScan *) inner_plan;
List *indxqualorig = innerscan->indxqualorig; List *indxqualorig = innerscan->indxqualorig;
/* No work needed if indxqual refers only to its own relation... */ /* No work needed if indxqual refers only to its own relation... */
...@@ -591,23 +622,23 @@ create_nestloop_node(NestPath *best_path, ...@@ -591,23 +622,23 @@ create_nestloop_node(NestPath *best_path,
NIL, NIL,
innerrel); innerrel);
/* fix the inner qpqual too, if it has join clauses */ /* fix the inner qpqual too, if it has join clauses */
if (NumRelids((Node *) inner_node->qual) > 1) if (NumRelids((Node *) inner_plan->qual) > 1)
inner_node->qual = join_references(inner_node->qual, inner_plan->qual = join_references(inner_plan->qual,
outer_tlist, outer_tlist,
NIL, NIL,
innerrel); innerrel);
} }
} }
else if (IsA(inner_node, TidScan)) else if (IsA(inner_plan, TidScan))
{ {
TidScan *innerscan = (TidScan *) inner_node; TidScan *innerscan = (TidScan *) inner_plan;
innerscan->tideval = join_references(innerscan->tideval, innerscan->tideval = join_references(innerscan->tideval,
outer_tlist, outer_tlist,
inner_tlist, inner_tlist,
innerscan->scan.scanrelid); innerscan->scan.scanrelid);
} }
else if (IsA_Join(inner_node)) else if (IsA_Join(inner_plan))
{ {
/* /*
...@@ -617,8 +648,8 @@ create_nestloop_node(NestPath *best_path, ...@@ -617,8 +648,8 @@ create_nestloop_node(NestPath *best_path,
* join --- how can we estimate whether this is a good thing to * join --- how can we estimate whether this is a good thing to
* do? * do?
*/ */
inner_node = (Plan *) make_material(inner_tlist, inner_plan = (Plan *) make_material(inner_tlist,
inner_node); inner_plan);
} }
/* /*
...@@ -633,30 +664,30 @@ create_nestloop_node(NestPath *best_path, ...@@ -633,30 +664,30 @@ create_nestloop_node(NestPath *best_path,
inner_tlist, inner_tlist,
(Index) 0); (Index) 0);
join_node = make_nestloop(tlist, join_plan = make_nestloop(tlist,
joinclauses, joinclauses,
otherclauses, otherclauses,
outer_node, outer_plan,
inner_node, inner_plan,
best_path->jointype); best_path->jointype);
copy_path_costsize(&join_node->join.plan, &best_path->path); copy_path_costsize(&join_plan->join.plan, &best_path->path);
return join_node; return join_plan;
} }
static MergeJoin * static MergeJoin *
create_mergejoin_node(MergePath *best_path, create_mergejoin_plan(MergePath *best_path,
List *tlist, List *tlist,
List *joinclauses, List *joinclauses,
List *otherclauses, List *otherclauses,
Plan *outer_node, Plan *outer_plan,
List *outer_tlist, List *outer_tlist,
Plan *inner_node, Plan *inner_plan,
List *inner_tlist) List *inner_tlist)
{ {
List *mergeclauses; List *mergeclauses;
MergeJoin *join_node; MergeJoin *join_plan;
mergeclauses = get_actual_clauses(best_path->path_mergeclauses); mergeclauses = get_actual_clauses(best_path->path_mergeclauses);
...@@ -692,15 +723,15 @@ create_mergejoin_node(MergePath *best_path, ...@@ -692,15 +723,15 @@ create_mergejoin_node(MergePath *best_path,
* necessary. The sort cost was already accounted for in the path. * necessary. The sort cost was already accounted for in the path.
*/ */
if (best_path->outersortkeys) if (best_path->outersortkeys)
outer_node = (Plan *) outer_plan = (Plan *)
make_sort_from_pathkeys(outer_tlist, make_sort_from_pathkeys(outer_tlist,
outer_node, outer_plan,
best_path->outersortkeys); best_path->outersortkeys);
if (best_path->innersortkeys) if (best_path->innersortkeys)
inner_node = (Plan *) inner_plan = (Plan *)
make_sort_from_pathkeys(inner_tlist, make_sort_from_pathkeys(inner_tlist,
inner_node, inner_plan,
best_path->innersortkeys); best_path->innersortkeys);
/* /*
...@@ -723,7 +754,7 @@ create_mergejoin_node(MergePath *best_path, ...@@ -723,7 +754,7 @@ create_mergejoin_node(MergePath *best_path,
* This check must agree with ExecMarkPos/ExecRestrPos in * This check must agree with ExecMarkPos/ExecRestrPos in
* executor/execAmi.c! * executor/execAmi.c!
*/ */
switch (nodeTag(inner_node)) switch (nodeTag(inner_plan))
{ {
case T_SeqScan: case T_SeqScan:
case T_IndexScan: case T_IndexScan:
...@@ -734,40 +765,40 @@ create_mergejoin_node(MergePath *best_path, ...@@ -734,40 +765,40 @@ create_mergejoin_node(MergePath *best_path,
default: default:
/* Ooops, need to materialize the inner plan */ /* Ooops, need to materialize the inner plan */
inner_node = (Plan *) make_material(inner_tlist, inner_plan = (Plan *) make_material(inner_tlist,
inner_node); inner_plan);
break; break;
} }
/* /*
* Now we can build the mergejoin node. * Now we can build the mergejoin node.
*/ */
join_node = make_mergejoin(tlist, join_plan = make_mergejoin(tlist,
joinclauses, joinclauses,
otherclauses, otherclauses,
mergeclauses, mergeclauses,
outer_node, outer_plan,
inner_node, inner_plan,
best_path->jpath.jointype); best_path->jpath.jointype);
copy_path_costsize(&join_node->join.plan, &best_path->jpath.path); copy_path_costsize(&join_plan->join.plan, &best_path->jpath.path);
return join_node; return join_plan;
} }
static HashJoin * static HashJoin *
create_hashjoin_node(HashPath *best_path, create_hashjoin_plan(HashPath *best_path,
List *tlist, List *tlist,
List *joinclauses, List *joinclauses,
List *otherclauses, List *otherclauses,
Plan *outer_node, Plan *outer_plan,
List *outer_tlist, List *outer_tlist,
Plan *inner_node, Plan *inner_plan,
List *inner_tlist) List *inner_tlist)
{ {
List *hashclauses; List *hashclauses;
HashJoin *join_node; HashJoin *join_plan;
Hash *hash_node; Hash *hash_plan;
Node *innerhashkey; Node *innerhashkey;
/* /*
...@@ -811,18 +842,18 @@ create_hashjoin_node(HashPath *best_path, ...@@ -811,18 +842,18 @@ create_hashjoin_node(HashPath *best_path,
/* /*
* Build the hash node and hash join node. * Build the hash node and hash join node.
*/ */
hash_node = make_hash(inner_tlist, innerhashkey, inner_node); hash_plan = make_hash(inner_tlist, innerhashkey, inner_plan);
join_node = make_hashjoin(tlist, join_plan = make_hashjoin(tlist,
joinclauses, joinclauses,
otherclauses, otherclauses,
hashclauses, hashclauses,
outer_node, outer_plan,
(Plan *) hash_node, (Plan *) hash_plan,
best_path->jpath.jointype); best_path->jpath.jointype);
copy_path_costsize(&join_node->join.plan, &best_path->jpath.path); copy_path_costsize(&join_plan->join.plan, &best_path->jpath.path);
return join_node; return join_plan;
} }
...@@ -1106,7 +1137,7 @@ copy_path_costsize(Plan *dest, Path *src) ...@@ -1106,7 +1137,7 @@ copy_path_costsize(Plan *dest, Path *src)
* but it helps produce more reasonable-looking EXPLAIN output. * but it helps produce more reasonable-looking EXPLAIN output.
* (Some callers alter the info after copying it.) * (Some callers alter the info after copying it.)
*/ */
void static void
copy_plan_costsize(Plan *dest, Plan *src) copy_plan_costsize(Plan *dest, Plan *src)
{ {
if (src) if (src)
...@@ -1128,6 +1159,10 @@ copy_plan_costsize(Plan *dest, Plan *src) ...@@ -1128,6 +1159,10 @@ copy_plan_costsize(Plan *dest, Plan *src)
/***************************************************************************** /*****************************************************************************
* *
* PLAN NODE BUILDING ROUTINES
*
* Some of these are exported because they are called to build plan nodes
* in contexts where we're not deriving the plan node from a path node.
* *
*****************************************************************************/ *****************************************************************************/
...@@ -1212,7 +1247,7 @@ make_subqueryscan(List *qptlist, ...@@ -1212,7 +1247,7 @@ make_subqueryscan(List *qptlist,
SubqueryScan *node = makeNode(SubqueryScan); SubqueryScan *node = makeNode(SubqueryScan);
Plan *plan = &node->scan.plan; Plan *plan = &node->scan.plan;
/* cost should be inserted by caller */ copy_plan_costsize(plan, subplan);
plan->state = (EState *) NULL; plan->state = (EState *) NULL;
plan->targetlist = qptlist; plan->targetlist = qptlist;
plan->qual = qpqual; plan->qual = qpqual;
...@@ -1225,6 +1260,39 @@ make_subqueryscan(List *qptlist, ...@@ -1225,6 +1260,39 @@ make_subqueryscan(List *qptlist,
return node; return node;
} }
Append *
make_append(List *appendplans, bool isTarget, List *tlist)
{
Append *node = makeNode(Append);
Plan *plan = &node->plan;
List *subnode;
/* compute costs from subplan costs */
plan->startup_cost = 0;
plan->total_cost = 0;
plan->plan_rows = 0;
plan->plan_width = 0;
foreach(subnode, appendplans)
{
Plan *subplan = (Plan *) lfirst(subnode);
if (subnode == appendplans) /* first node? */
plan->startup_cost = subplan->startup_cost;
plan->total_cost += subplan->total_cost;
plan->plan_rows += subplan->plan_rows;
if (plan->plan_width < subplan->plan_width)
plan->plan_width = subplan->plan_width;
}
plan->state = (EState *) NULL;
plan->targetlist = tlist;
plan->qual = NIL;
plan->lefttree = NULL;
plan->righttree = NULL;
node->appendplans = appendplans;
node->isTarget = isTarget;
return node;
}
static NestLoop * static NestLoop *
make_nestloop(List *tlist, make_nestloop(List *tlist,
......
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planmain.c,v 1.61 2000/10/05 19:11:29 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planmain.c,v 1.62 2000/11/12 00:36:58 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -65,7 +65,8 @@ static Plan *subplanner(Query *root, List *flat_tlist, ...@@ -65,7 +65,8 @@ static Plan *subplanner(Query *root, List *flat_tlist,
* tuple_fraction >= 1: tuple_fraction is the absolute number of tuples * tuple_fraction >= 1: tuple_fraction is the absolute number of tuples
* expected to be retrieved (ie, a LIMIT specification) * expected to be retrieved (ie, a LIMIT specification)
* Note that while this routine and its subroutines treat a negative * Note that while this routine and its subroutines treat a negative
* tuple_fraction the same as 0, union_planner has a different interpretation. * tuple_fraction the same as 0, grouping_planner has a different
* interpretation.
* *
* Returns a query plan. * Returns a query plan.
*-------------------- *--------------------
...@@ -125,9 +126,16 @@ query_planner(Query *root, ...@@ -125,9 +126,16 @@ query_planner(Query *root,
subplan = subplanner(root, var_only_tlist, tuple_fraction); subplan = subplanner(root, var_only_tlist, tuple_fraction);
/* /*
* Build a result node to control the plan if we have constant quals. * Build a result node to control the plan if we have constant quals,
* or if the top-level plan node is one that cannot do expression
* evaluation (it won't be able to evaluate the requested tlist).
* Currently, the only plan node we might see here that falls into
* that category is Append.
*
* XXX future improvement: if the given tlist is flat anyway, we don't
* really need a Result node.
*/ */
if (constant_quals) if (constant_quals || IsA(subplan, Append))
{ {
/* /*
...@@ -325,8 +333,8 @@ subplanner(Query *root, ...@@ -325,8 +333,8 @@ subplanner(Query *root,
/* /*
* Nothing for it but to sort the cheapest-total-cost path --- but we * Nothing for it but to sort the cheapest-total-cost path --- but we
* let the caller do that. union_planner has to be able to add a sort * let the caller do that. grouping_planner has to be able to add a
* node anyway, so no need for extra code here. (Furthermore, the * sort node anyway, so no need for extra code here. (Furthermore, the
* given pathkeys might involve something we can't compute here, such * given pathkeys might involve something we can't compute here, such
* as an aggregate function...) * as an aggregate function...)
*/ */
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.95 2000/11/09 02:46:16 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.96 2000/11/12 00:36:58 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -43,6 +43,8 @@ static void resolvenew_in_jointree(Node *jtnode, int varno, List *subtlist); ...@@ -43,6 +43,8 @@ static void resolvenew_in_jointree(Node *jtnode, int varno, List *subtlist);
static Node *preprocess_jointree(Query *parse, Node *jtnode); static Node *preprocess_jointree(Query *parse, Node *jtnode);
static Node *preprocess_expression(Query *parse, Node *expr, int kind); static Node *preprocess_expression(Query *parse, Node *expr, int kind);
static void preprocess_qual_conditions(Query *parse, Node *jtnode); static void preprocess_qual_conditions(Query *parse, Node *jtnode);
static Plan *inheritance_planner(Query *parse, List *inheritlist);
static Plan *grouping_planner(Query *parse, double tuple_fraction);
static List *make_subplanTargetList(Query *parse, List *tlist, static List *make_subplanTargetList(Query *parse, List *tlist,
AttrNumber **groupColIdx); AttrNumber **groupColIdx);
static Plan *make_groupplan(List *group_tlist, bool tuplePerGroup, static Plan *make_groupplan(List *group_tlist, bool tuplePerGroup,
...@@ -65,7 +67,7 @@ planner(Query *parse) ...@@ -65,7 +67,7 @@ planner(Query *parse)
/* /*
* The planner can be called recursively (an example is when * The planner can be called recursively (an example is when
* eval_const_expressions tries to simplify an SQL function). * eval_const_expressions tries to pre-evaluate an SQL function).
* So, these global state variables must be saved and restored. * So, these global state variables must be saved and restored.
* *
* These vars cannot be moved into the Query structure since their * These vars cannot be moved into the Query structure since their
...@@ -109,11 +111,14 @@ planner(Query *parse) ...@@ -109,11 +111,14 @@ planner(Query *parse)
* *
* parse is the querytree produced by the parser & rewriter. * parse is the querytree produced by the parser & rewriter.
* tuple_fraction is the fraction of tuples we expect will be retrieved. * tuple_fraction is the fraction of tuples we expect will be retrieved.
* tuple_fraction is interpreted as explained for union_planner, below. * tuple_fraction is interpreted as explained for grouping_planner, below.
* *
* Basically, this routine does the stuff that should only be done once * Basically, this routine does the stuff that should only be done once
* per Query object. It then calls union_planner, which may be called * per Query object. It then calls grouping_planner. At one time,
* recursively on the same Query node in order to handle inheritance. * grouping_planner could be invoked recursively on the same Query object;
* that's not currently true, but we keep the separation between the two
* routines anyway, in case we need it again someday.
*
* subquery_planner will be called recursively to handle sub-Query nodes * subquery_planner will be called recursively to handle sub-Query nodes
* found within the query's expressions and rangetable. * found within the query's expressions and rangetable.
* *
...@@ -164,7 +169,7 @@ subquery_planner(Query *parse, double tuple_fraction) ...@@ -164,7 +169,7 @@ subquery_planner(Query *parse, double tuple_fraction)
} }
/* /*
* Do preprocessing on targetlist and quals. * Do expression preprocessing on targetlist and quals.
*/ */
parse->targetList = (List *) parse->targetList = (List *)
preprocess_expression(parse, (Node *) parse->targetList, preprocess_expression(parse, (Node *) parse->targetList,
...@@ -176,17 +181,14 @@ subquery_planner(Query *parse, double tuple_fraction) ...@@ -176,17 +181,14 @@ subquery_planner(Query *parse, double tuple_fraction)
EXPRKIND_HAVING); EXPRKIND_HAVING);
/* /*
* Do the main planning (potentially recursive for inheritance) * Do the main planning. If we have an inherited target relation,
*/ * that needs special processing, else go straight to grouping_planner.
plan = union_planner(parse, tuple_fraction);
/*
* XXX should any more of union_planner's activity be moved here?
*
* That would take careful study of the interactions with prepunion.c,
* but I suspect it would pay off in simplicity and avoidance of
* wasted cycles.
*/ */
if (parse->resultRelation &&
(lst = expand_inherted_rtentry(parse, parse->resultRelation)) != NIL)
plan = inheritance_planner(parse, lst);
else
plan = grouping_planner(parse, tuple_fraction);
/* /*
* If any subplans were generated, or if we're inside a subplan, * If any subplans were generated, or if we're inside a subplan,
...@@ -600,10 +602,65 @@ preprocess_qual_conditions(Query *parse, Node *jtnode) ...@@ -600,10 +602,65 @@ preprocess_qual_conditions(Query *parse, Node *jtnode)
} }
/*-------------------- /*--------------------
* union_planner * inheritance_planner
* Invokes the planner on union-type queries (both set operations and * Generate a plan in the case where the result relation is an
* appends produced by inheritance), recursing if necessary to get them * inheritance set.
* all, then processes normal plans. *
* We have to handle this case differently from cases where a source
* relation is an inheritance set. Source inheritance is expanded at
* the bottom of the plan tree (see allpaths.c), but target inheritance
* has to be expanded at the top. The reason is that for UPDATE, each
* target relation needs a different targetlist matching its own column
* set. (This is not so critical for DELETE, but for simplicity we treat
* inherited DELETE the same way.) Fortunately, the UPDATE/DELETE target
* can never be the nullable side of an outer join, so it's OK to generate
* the plan this way.
*
* parse is the querytree produced by the parser & rewriter.
* inheritlist is an integer list of RT indexes for the result relation set.
*
* Returns a query plan.
*--------------------
*/
static Plan *
inheritance_planner(Query *parse, List *inheritlist)
{
int parentRTindex = parse->resultRelation;
Oid parentOID = getrelid(parentRTindex, parse->rtable);
List *subplans = NIL;
List *tlist = NIL;
List *l;
foreach(l, inheritlist)
{
int childRTindex = lfirsti(l);
Oid childOID = getrelid(childRTindex, parse->rtable);
Query *subquery;
Plan *subplan;
/* Generate modified query with this rel as target */
subquery = (Query *) adjust_inherited_attrs((Node *) parse,
parentRTindex, parentOID,
childRTindex, childOID);
/* Generate plan */
subplan = grouping_planner(subquery, 0.0 /* retrieve all tuples */);
subplans = lappend(subplans, subplan);
/* Save preprocessed tlist from first rel for use in Append */
if (tlist == NIL)
tlist = subplan->targetlist;
}
/* Save the target-relations list for the executor, too */
parse->resultRelations = inheritlist;
return (Plan *) make_append(subplans, true, tlist);
}
/*--------------------
* grouping_planner
* Perform planning steps related to grouping, aggregation, etc.
* This primarily means adding top-level processing to the basic
* query plan produced by query_planner.
* *
* parse is the querytree produced by the parser & rewriter. * parse is the querytree produced by the parser & rewriter.
* tuple_fraction is the fraction of tuples we expect will be retrieved * tuple_fraction is the fraction of tuples we expect will be retrieved
...@@ -621,18 +678,15 @@ preprocess_qual_conditions(Query *parse, Node *jtnode) ...@@ -621,18 +678,15 @@ preprocess_qual_conditions(Query *parse, Node *jtnode)
* Returns a query plan. * Returns a query plan.
*-------------------- *--------------------
*/ */
Plan * static Plan *
union_planner(Query *parse, grouping_planner(Query *parse, double tuple_fraction)
double tuple_fraction)
{ {
List *tlist = parse->targetList; List *tlist = parse->targetList;
Plan *result_plan = (Plan *) NULL; Plan *result_plan;
AttrNumber *groupColIdx = NULL; List *current_pathkeys;
List *current_pathkeys = NIL;
List *group_pathkeys; List *group_pathkeys;
List *sort_pathkeys; List *sort_pathkeys;
Index rt_index; AttrNumber *groupColIdx = NULL;
List *inheritors;
if (parse->setOperations) if (parse->setOperations)
{ {
...@@ -654,12 +708,13 @@ union_planner(Query *parse, ...@@ -654,12 +708,13 @@ union_planner(Query *parse,
tlist = postprocess_setop_tlist(result_plan->targetlist, tlist); tlist = postprocess_setop_tlist(result_plan->targetlist, tlist);
/* /*
* We leave current_pathkeys NIL indicating we do not know sort * We set current_pathkeys NIL indicating we do not know sort
* order. This is correct when the top set operation is UNION ALL, * order. This is correct when the top set operation is UNION ALL,
* since the appended-together results are unsorted even if the * since the appended-together results are unsorted even if the
* subplans were sorted. For other set operations we could be * subplans were sorted. For other set operations we could be
* smarter --- future improvement! * smarter --- room for future improvement!
*/ */
current_pathkeys = NIL;
/* /*
* Calculate pathkeys that represent grouping/ordering * Calculate pathkeys that represent grouping/ordering
...@@ -670,54 +725,6 @@ union_planner(Query *parse, ...@@ -670,54 +725,6 @@ union_planner(Query *parse,
sort_pathkeys = make_pathkeys_for_sortclauses(parse->sortClause, sort_pathkeys = make_pathkeys_for_sortclauses(parse->sortClause,
tlist); tlist);
} }
else if (find_inheritable_rt_entry(parse->rtable,
&rt_index, &inheritors))
{
List *sub_tlist;
/*
* Generate appropriate target list for subplan; may be different
* from tlist if grouping or aggregation is needed.
*/
sub_tlist = make_subplanTargetList(parse, tlist, &groupColIdx);
/*
* Recursively plan the subqueries needed for inheritance
*/
result_plan = plan_inherit_queries(parse, sub_tlist,
rt_index, inheritors);
/*
* Fix up outer target list. NOTE: unlike the case for
* non-inherited query, we pass the unfixed tlist to subplans,
* which do their own fixing. But we still want to fix the outer
* target list afterwards. I *think* this is correct --- doing the
* fix before recursing is definitely wrong, because
* preprocess_targetlist() will do the wrong thing if invoked
* twice on the same list. Maybe that is a bug? tgl 6/6/99
*/
tlist = preprocess_targetlist(tlist,
parse->commandType,
parse->resultRelation,
parse->rtable);
if (parse->rowMarks)
elog(ERROR, "SELECT FOR UPDATE is not supported for inherit queries");
/*
* We leave current_pathkeys NIL indicating we do not know sort
* order of the Append-ed results.
*/
/*
* Calculate pathkeys that represent grouping/ordering
* requirements
*/
group_pathkeys = make_pathkeys_for_sortclauses(parse->groupClause,
tlist);
sort_pathkeys = make_pathkeys_for_sortclauses(parse->sortClause,
tlist);
}
else else
{ {
List *sub_tlist; List *sub_tlist;
...@@ -938,10 +945,6 @@ union_planner(Query *parse, ...@@ -938,10 +945,6 @@ union_planner(Query *parse,
current_pathkeys = parse->query_pathkeys; current_pathkeys = parse->query_pathkeys;
} }
/* query_planner returns NULL if it thinks plan is bogus */
if (!result_plan)
elog(ERROR, "union_planner: failed to create plan");
/* /*
* We couldn't canonicalize group_pathkeys and sort_pathkeys before * We couldn't canonicalize group_pathkeys and sort_pathkeys before
* running query_planner(), so do it now. * running query_planner(), so do it now.
...@@ -1057,7 +1060,7 @@ union_planner(Query *parse, ...@@ -1057,7 +1060,7 @@ union_planner(Query *parse,
* make_subplanTargetList * make_subplanTargetList
* Generate appropriate target list when grouping is required. * Generate appropriate target list when grouping is required.
* *
* When union_planner inserts Aggregate and/or Group plan nodes above * When grouping_planner inserts Aggregate and/or Group plan nodes above
* the result of query_planner, we typically want to pass a different * the result of query_planner, we typically want to pass a different
* target list to query_planner than the outer plan nodes should have. * target list to query_planner than the outer plan nodes should have.
* This routine generates the correct target list for the subplan. * This routine generates the correct target list for the subplan.
......
/*------------------------------------------------------------------------- /*-------------------------------------------------------------------------
* *
* prepunion.c * prepunion.c
* Routines to plan set-operation and inheritance queries. The filename * Routines to plan set-operation queries. The filename is a leftover
* is a leftover from a time when only UNIONs were handled. * from a time when only UNIONs were implemented.
*
* There is also some code here to support planning of queries that use
* inheritance (SELECT FROM foo*). This no longer has much connection
* to the processing of UNION queries, but it's still here.
*
* *
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepunion.c,v 1.55 2000/11/09 02:46:17 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepunion.c,v 1.56 2000/11/12 00:36:59 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -30,13 +35,19 @@ ...@@ -30,13 +35,19 @@
#include "parser/parsetree.h" #include "parser/parsetree.h"
#include "utils/lsyscache.h" #include "utils/lsyscache.h"
/* macros borrowed from expression_tree_mutator */
#define FLATCOPY(newnode, node, nodetype) \
( (newnode) = makeNode(nodetype), \
memcpy((newnode), (node), sizeof(nodetype)) )
typedef struct typedef struct
{ {
Index rt_index; Index old_rt_index;
int sublevels_up; Index new_rt_index;
Oid old_relid; Oid old_relid;
Oid new_relid; Oid new_relid;
} fix_parsetree_attnums_context; } adjust_inherited_attrs_context;
static Plan *recurse_set_operations(Node *setOp, Query *parse, static Plan *recurse_set_operations(Node *setOp, Query *parse,
List *colTypes, bool junkOK, List *colTypes, bool junkOK,
...@@ -53,14 +64,8 @@ static List *generate_setop_tlist(List *colTypes, int flag, ...@@ -53,14 +64,8 @@ static List *generate_setop_tlist(List *colTypes, int flag,
List *input_tlist, List *input_tlist,
List *refnames_tlist); List *refnames_tlist);
static bool tlist_same_datatypes(List *tlist, List *colTypes, bool junkOK); static bool tlist_same_datatypes(List *tlist, List *colTypes, bool junkOK);
static void fix_parsetree_attnums(Index rt_index, Oid old_relid, static Node *adjust_inherited_attrs_mutator(Node *node,
Oid new_relid, Query *parsetree); adjust_inherited_attrs_context *context);
static bool fix_parsetree_attnums_walker(Node *node,
fix_parsetree_attnums_context *context);
static RangeTblEntry *new_rangetable_entry(Oid new_relid,
RangeTblEntry *old_entry);
static Append *make_append(List *appendplans, Index rt_index,
List *inheritrtable, List *tlist);
/* /*
...@@ -69,8 +74,8 @@ static Append *make_append(List *appendplans, Index rt_index, ...@@ -69,8 +74,8 @@ static Append *make_append(List *appendplans, Index rt_index,
* Plans the queries for a tree of set operations (UNION/INTERSECT/EXCEPT) * Plans the queries for a tree of set operations (UNION/INTERSECT/EXCEPT)
* *
* This routine only deals with the setOperations tree of the given query. * This routine only deals with the setOperations tree of the given query.
* Any top-level ORDER BY requested in parse->sortClause will be added on * Any top-level ORDER BY requested in parse->sortClause will be added
* back in union_planner. * when we return to grouping_planner.
*/ */
Plan * Plan *
plan_set_operations(Query *parse) plan_set_operations(Query *parse)
...@@ -142,7 +147,6 @@ recurse_set_operations(Node *setOp, Query *parse, ...@@ -142,7 +147,6 @@ recurse_set_operations(Node *setOp, Query *parse,
NIL, NIL,
rtr->rtindex, rtr->rtindex,
subplan); subplan);
copy_plan_costsize(plan, subplan);
return plan; return plan;
} }
else if (IsA(setOp, SetOperationStmt)) else if (IsA(setOp, SetOperationStmt))
...@@ -217,8 +221,7 @@ generate_union_plan(SetOperationStmt *op, Query *parse, ...@@ -217,8 +221,7 @@ generate_union_plan(SetOperationStmt *op, Query *parse,
*/ */
plan = (Plan *) plan = (Plan *)
make_append(planlist, make_append(planlist,
0, false,
NIL,
generate_setop_tlist(op->colTypes, -1, false, generate_setop_tlist(op->colTypes, -1, false,
((Plan *) lfirst(planlist))->targetlist, ((Plan *) lfirst(planlist))->targetlist,
refnames_tlist)); refnames_tlist));
...@@ -269,8 +272,7 @@ generate_nonunion_plan(SetOperationStmt *op, Query *parse, ...@@ -269,8 +272,7 @@ generate_nonunion_plan(SetOperationStmt *op, Query *parse,
*/ */
plan = (Plan *) plan = (Plan *)
make_append(makeList2(lplan, rplan), make_append(makeList2(lplan, rplan),
0, false,
NIL,
generate_setop_tlist(op->colTypes, 0, false, generate_setop_tlist(op->colTypes, 0, false,
lplan->targetlist, lplan->targetlist,
refnames_tlist)); refnames_tlist));
...@@ -456,132 +458,6 @@ tlist_same_datatypes(List *tlist, List *colTypes, bool junkOK) ...@@ -456,132 +458,6 @@ tlist_same_datatypes(List *tlist, List *colTypes, bool junkOK)
} }
/*
* plan_inherit_queries
* Plans the queries for an inheritance tree rooted at a parent relation.
*
* Inputs:
* root = parent parse tree
* tlist = target list for inheritance subqueries (not same as parent's!)
* rt_index = rangetable index for current inheritance item
* inheritors = list of OIDs of the target rel plus all its descendants
*
* Returns an APPEND node that forms the result of performing the given
* query for each member relation of the inheritance group.
*
* If grouping, aggregation, or sorting is specified in the parent plan,
* the subplans should not do any of those steps --- we must do those
* operations just once above the APPEND node. The given tlist has been
* modified appropriately to remove group/aggregate expressions, but the
* Query node still has the relevant fields set. We remove them in the
* copies used for subplans.
*
* NOTE: this can be invoked recursively if more than one inheritance wildcard
* is present. At each level of recursion, the first wildcard remaining in
* the rangetable is expanded.
*
* NOTE: don't bother optimizing this routine for the case that the target
* rel has no children. We won't get here unless find_inheritable_rt_entry
* found at least two members in the inheritance group, so an APPEND is
* certainly necessary.
*/
Plan *
plan_inherit_queries(Query *root, List *tlist,
Index rt_index, List *inheritors)
{
RangeTblEntry *rt_entry = rt_fetch(rt_index, root->rtable);
List *union_plans = NIL;
List *union_rtentries = NIL;
List *save_tlist = root->targetList;
double tuple_fraction;
List *i;
/*
* Avoid making copies of the root's tlist, which we aren't going to
* use anyway (we are going to make copies of the passed tlist,
* instead). This is purely a space-saving hack. Note we restore
* the root's tlist before exiting.
*/
root->targetList = NIL;
/*
* If we are going to need sorting or grouping at the top level, force
* lower-level planners to assume that all tuples will be retrieved.
*/
if (root->distinctClause || root->sortClause ||
root->groupClause || root->hasAggs)
tuple_fraction = 0.0; /* will need all tuples from each subplan */
else
tuple_fraction = -1.0; /* default behavior is OK (I think) */
foreach(i, inheritors)
{
Oid relid = lfirsti(i);
/*
* Make a modifiable copy of the original query, and replace the
* target rangetable entry in it with a new one identifying this
* child table. The new rtentry is marked inh = false --- this
* is essential to prevent infinite recursion when the subquery
* is rescanned by find_inheritable_rt_entry!
*/
Query *new_root = copyObject(root);
RangeTblEntry *new_rt_entry = new_rangetable_entry(relid,
rt_entry);
new_rt_entry->inh = false;
rt_store(rt_index, new_root->rtable, new_rt_entry);
/*
* Insert (a modifiable copy of) the desired simplified tlist into
* the subquery
*/
new_root->targetList = copyObject(tlist);
/*
* Clear the sorting and grouping qualifications in the subquery,
* so that sorting will only be done once after append
*/
new_root->distinctClause = NIL;
new_root->sortClause = NIL;
new_root->groupClause = NIL;
new_root->havingQual = NULL;
new_root->limitOffset = NULL; /* LIMIT's probably unsafe too */
new_root->limitCount = NULL;
new_root->hasAggs = false; /* shouldn't be any left ... */
/*
* Update attribute numbers in case child has different ordering
* of columns than parent (as can happen after ALTER TABLE).
*
* XXX This is a crock, and it doesn't really work. It'd be better
* to fix ALTER TABLE to preserve consistency of attribute
* numbering.
*/
fix_parsetree_attnums(rt_index,
rt_entry->relid,
relid,
new_root);
/*
* Plan the subquery by recursively calling union_planner().
* Add plan and child rtentry to lists for APPEND.
*/
union_plans = lappend(union_plans,
union_planner(new_root, tuple_fraction));
union_rtentries = lappend(union_rtentries, new_rt_entry);
}
/* Restore root's tlist */
root->targetList = save_tlist;
/* Construct the finished Append plan. */
return (Plan *) make_append(union_plans,
rt_index,
union_rtentries,
((Plan *) lfirst(union_plans))->targetlist);
}
/* /*
* find_all_inheritors - * find_all_inheritors -
* Returns an integer list of relids including the given rel plus * Returns an integer list of relids including the given rel plus
...@@ -622,200 +498,181 @@ find_all_inheritors(Oid parentrel) ...@@ -622,200 +498,181 @@ find_all_inheritors(Oid parentrel)
} }
/* /*
* find_inheritable_rt_entry - * expand_inherted_rtentry
* Given a rangetable, find the first rangetable entry that represents * Check whether a rangetable entry represents an inheritance set.
* an inheritance set. * If so, add entries for all the child tables to the query's
* * rangetable, and return an integer list of RT indexes for the
* If successful, set *rt_index to the index (1..n) of the entry, * whole inheritance set (parent and children).
* set *inheritors to a list of the relation OIDs of the set, * If not, return NIL.
* and return TRUE.
*
* If there is no entry that requires inheritance processing,
* return FALSE.
* *
* NOTE: We return the inheritors list so that plan_inherit_queries doesn't * A childless table is never considered to be an inheritance set; therefore
* have to compute it again. * the result will never be a one-element list. It'll be either empty
* or have two or more elements.
* *
* NOTE: We clear the inh flag in any entries that have it set but turn * NOTE: after this routine executes, the specified RTE will always have
* out not to have any actual inheritance children. This is an efficiency * its inh flag cleared, whether or not there were any children. This
* hack to avoid having to repeat the inheritance checks if the list is * ensures we won't expand the same RTE twice, which would otherwise occur
* scanned again (as will happen during expansion of any subsequent entry * for the case of an inherited UPDATE/DELETE target relation.
* that does have inheritance children). Although modifying the input
* rangetable in-place may seem uncool, there's no reason not to do it,
* since any re-examination of the entry would just come to the same
* conclusion that the table has no children.
*/ */
bool List *
find_inheritable_rt_entry(List *rangetable, expand_inherted_rtentry(Query *parse, Index rti)
Index *rt_index,
List **inheritors)
{ {
Index count = 0; RangeTblEntry *rte = rt_fetch(rti, parse->rtable);
List *temp; Oid parentOID = rte->relid;
List *inhOIDs;
foreach(temp, rangetable) List *inhRTIs;
List *l;
/* Does RT entry allow inheritance? */
if (! rte->inh)
return NIL;
Assert(parentOID != InvalidOid && rte->subquery == NULL);
/* Always clear the parent's inh flag, see above comments */
rte->inh = false;
/* Fast path for common case of childless table */
if (! has_subclass(parentOID))
return NIL;
/* Scan for all members of inheritance set */
inhOIDs = find_all_inheritors(parentOID);
/*
* Check that there's at least one descendant, else treat as
* no-child case. This could happen despite above has_subclass()
* check, if table once had a child but no longer does.
*/
if (lnext(inhOIDs) == NIL)
return NIL;
/* OK, it's an inheritance set; expand it */
inhRTIs = makeListi1(rti);
foreach(l, inhOIDs)
{ {
RangeTblEntry *rt_entry = (RangeTblEntry *) lfirst(temp); Oid childOID = (Oid) lfirsti(l);
List *inhs; RangeTblEntry *childrte;
Index childRTindex;
count++; /* parent will be in the list too, so ignore it */
/* Ignore non-inheritable RT entries */ if (childOID == parentOID)
if (! rt_entry->inh)
continue;
/* Fast path for common case of childless table */
if (! has_subclass(rt_entry->relid))
{
rt_entry->inh = false;
continue; continue;
}
/* Scan for all members of inheritance set */
inhs = find_all_inheritors(rt_entry->relid);
/* /*
* Check that there's at least one descendant, else treat as * Build an RTE for the child, and attach to query's rangetable list.
* no-child case. This could happen despite above has_subclass() * We copy most fields of the parent's RTE, but replace relation
* check, if table once had a child but no longer does. * real name and OID. Note that inh will be false at this point.
*/ */
if (lnext(inhs) == NIL) childrte = copyObject(rte);
{ childrte->relname = get_rel_name(childOID);
rt_entry->inh = false; childrte->relid = childOID;
continue; parse->rtable = lappend(parse->rtable, childrte);
} childRTindex = length(parse->rtable);
/* OK, found our boy */
*rt_index = count;
*inheritors = inhs;
return true;
}
return false; inhRTIs = lappendi(inhRTIs, childRTindex);
} }
return inhRTIs;
/*
* new_rangetable_entry -
* Replaces the name and relid of 'old_entry' with the values for
* 'new_relid'.
*
* Returns a copy of 'old_entry' with the parameters substituted.
*/
static RangeTblEntry *
new_rangetable_entry(Oid new_relid, RangeTblEntry *old_entry)
{
RangeTblEntry *new_entry = copyObject(old_entry);
/* Replace relation real name and OID, but not the reference name */
new_entry->relname = get_rel_name(new_relid);
new_entry->relid = new_relid;
return new_entry;
} }
/* /*
* fix_parsetree_attnums * adjust_inherited_attrs
* Replaces attribute numbers from the relation represented by * Copy the specified query or expression and translate Vars referring
* 'old_relid' in 'parsetree' with the attribute numbers from * to old_rt_index to refer to new_rt_index.
* 'new_relid'.
* *
* The parsetree is MODIFIED IN PLACE. This is OK only because * We also adjust varattno to match the new table by column name, rather
* plan_inherit_queries made a copy of the tree for us to hack upon. * than column number. This hack makes it possible for child tables to have
* different column positions for the "same" attribute as a parent, which
* helps ALTER TABLE ADD COLUMN. Unfortunately this isn't nearly enough to
* make it work transparently; there are other places where things fall down
* if children and parents don't have the same column numbers for inherited
* attributes. It'd be better to rip this code out and fix ALTER TABLE...
*/ */
static void Node *
fix_parsetree_attnums(Index rt_index, adjust_inherited_attrs(Node *node,
Oid old_relid, Index old_rt_index, Oid old_relid,
Oid new_relid, Index new_rt_index, Oid new_relid)
Query *parsetree)
{ {
fix_parsetree_attnums_context context; adjust_inherited_attrs_context context;
if (old_relid == new_relid) /* Handle simple case simply... */
return; /* no work needed for parent rel itself */ if (old_rt_index == new_rt_index)
{
Assert(old_relid == new_relid);
return copyObject(node);
}
context.rt_index = rt_index; context.old_rt_index = old_rt_index;
context.new_rt_index = new_rt_index;
context.old_relid = old_relid; context.old_relid = old_relid;
context.new_relid = new_relid; context.new_relid = new_relid;
context.sublevels_up = 0;
query_tree_walker(parsetree, /*
fix_parsetree_attnums_walker, * Must be prepared to start with a Query or a bare expression tree.
(void *) &context, */
true); if (node && IsA(node, Query))
{
Query *query = (Query *) node;
Query *newnode;
FLATCOPY(newnode, query, Query);
if (newnode->resultRelation == old_rt_index)
newnode->resultRelation = new_rt_index;
query_tree_mutator(newnode, adjust_inherited_attrs_mutator,
(void *) &context, false);
return (Node *) newnode;
}
else
return adjust_inherited_attrs_mutator(node, &context);
} }
/* static Node *
* Adjust varnos for child tables. This routine makes it possible for adjust_inherited_attrs_mutator(Node *node,
* child tables to have different column positions for the "same" attribute adjust_inherited_attrs_context *context)
* as a parent, which helps ALTER TABLE ADD COLUMN. Unfortunately this isn't
* nearly enough to make it work transparently; there are other places where
* things fall down if children and parents don't have the same column numbers
* for inherited attributes. It'd be better to rip this code out and fix
* ALTER TABLE...
*/
static bool
fix_parsetree_attnums_walker(Node *node,
fix_parsetree_attnums_context *context)
{ {
if (node == NULL) if (node == NULL)
return false; return NULL;
if (IsA(node, Var)) if (IsA(node, Var))
{ {
Var *var = (Var *) node; Var *var = (Var *) copyObject(node);
if (var->varlevelsup == context->sublevels_up && if (var->varlevelsup == 0 &&
var->varno == context->rt_index && var->varno == context->old_rt_index)
var->varattno > 0)
{ {
var->varattno = get_attnum(context->new_relid, var->varno = context->new_rt_index;
get_attname(context->old_relid, if (var->varattno > 0)
var->varattno)); var->varattno = get_attnum(context->new_relid,
get_attname(context->old_relid,
var->varattno));
} }
return false; return (Node *) var;
} }
if (IsA(node, Query)) if (IsA(node, RangeTblRef))
{ {
/* Recurse into subselects */ RangeTblRef *rtr = (RangeTblRef *) copyObject(node);
bool result;
context->sublevels_up++;
result = query_tree_walker((Query *) node,
fix_parsetree_attnums_walker,
(void *) context,
true);
context->sublevels_up--;
return result;
}
return expression_tree_walker(node, fix_parsetree_attnums_walker,
(void *) context);
}
static Append * if (rtr->rtindex == context->old_rt_index)
make_append(List *appendplans, rtr->rtindex = context->new_rt_index;
Index rt_index, return (Node *) rtr;
List *inheritrtable,
List *tlist)
{
Append *node = makeNode(Append);
List *subnode;
node->appendplans = appendplans;
node->inheritrelid = rt_index;
node->inheritrtable = inheritrtable;
node->plan.startup_cost = 0;
node->plan.total_cost = 0;
node->plan.plan_rows = 0;
node->plan.plan_width = 0;
foreach(subnode, appendplans)
{
Plan *subplan = (Plan *) lfirst(subnode);
if (subnode == appendplans) /* first node? */
node->plan.startup_cost = subplan->startup_cost;
node->plan.total_cost += subplan->total_cost;
node->plan.plan_rows += subplan->plan_rows;
if (node->plan.plan_width < subplan->plan_width)
node->plan.plan_width = subplan->plan_width;
} }
node->plan.state = (EState *) NULL; /*
node->plan.targetlist = tlist; * We have to process RestrictInfo nodes specially: we do NOT want to
node->plan.qual = NIL; * copy the original subclauseindices list, since the new rel may have
node->plan.lefttree = (Plan *) NULL; * different indices. The list will be rebuilt during planning anyway.
node->plan.righttree = (Plan *) NULL; */
if (IsA(node, RestrictInfo))
{
RestrictInfo *oldinfo = (RestrictInfo *) node;
RestrictInfo *newinfo = makeNode(RestrictInfo);
/* Copy all flat-copiable fields */
memcpy(newinfo, oldinfo, sizeof(RestrictInfo));
newinfo->clause = (Expr *)
adjust_inherited_attrs_mutator((Node *) oldinfo->clause, context);
return node; newinfo->subclauseindices = NIL;
return (Node *) newinfo;
}
/*
* NOTE: we do not need to recurse into sublinks, because they should
* already have been converted to subplans before we see them.
*/
return expression_tree_mutator(node, adjust_inherited_attrs_mutator,
(void *) context);
} }
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/pathnode.c,v 1.67 2000/10/05 19:48:27 momjian Exp $ * $Header: /cvsroot/pgsql/src/backend/optimizer/util/pathnode.c,v 1.68 2000/11/12 00:36:59 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -390,6 +390,37 @@ create_tidscan_path(RelOptInfo *rel, List *tideval) ...@@ -390,6 +390,37 @@ create_tidscan_path(RelOptInfo *rel, List *tideval)
return pathnode; return pathnode;
} }
/*
* create_append_path
* Creates a path corresponding to an Append plan, returning the
* pathnode.
*
*/
AppendPath *
create_append_path(RelOptInfo *rel, List *subpaths)
{
AppendPath *pathnode = makeNode(AppendPath);
List *l;
pathnode->path.pathtype = T_Append;
pathnode->path.parent = rel;
pathnode->path.pathkeys = NIL; /* result is always considered unsorted */
pathnode->subpaths = subpaths;
pathnode->path.startup_cost = 0;
pathnode->path.total_cost = 0;
foreach(l, subpaths)
{
Path *subpath = (Path *) lfirst(l);
if (l == subpaths) /* first node? */
pathnode->path.startup_cost = subpath->startup_cost;
pathnode->path.total_cost += subpath->total_cost;
}
return pathnode;
}
/* /*
* create_subqueryscan_path * create_subqueryscan_path
* Creates a path corresponding to a sequential scan of a subquery, * Creates a path corresponding to a sequential scan of a subquery,
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/relnode.c,v 1.29 2000/09/29 18:21:23 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/optimizer/util/relnode.c,v 1.30 2000/11/12 00:36:59 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -45,7 +45,6 @@ get_base_rel(Query *root, int relid) ...@@ -45,7 +45,6 @@ get_base_rel(Query *root, int relid)
{ {
List *baserels; List *baserels;
RelOptInfo *rel; RelOptInfo *rel;
Oid relationObjectId;
foreach(baserels, root->base_rel_list) foreach(baserels, root->base_rel_list)
{ {
...@@ -60,7 +59,30 @@ get_base_rel(Query *root, int relid) ...@@ -60,7 +59,30 @@ get_base_rel(Query *root, int relid)
} }
/* No existing RelOptInfo for this base rel, so make a new one */ /* No existing RelOptInfo for this base rel, so make a new one */
rel = makeNode(RelOptInfo); rel = make_base_rel(root, relid);
/* and add it to the list */
root->base_rel_list = lcons(rel, root->base_rel_list);
return rel;
}
/*
* make_base_rel
* Construct a base-relation RelOptInfo for the specified rangetable index.
*
* This is split out of get_base_rel so that inheritance-tree processing can
* construct baserel nodes for child tables. We need a RelOptInfo so we can
* plan a suitable access path for each child table, but we do NOT want to
* enter the child nodes into base_rel_list. In most contexts, get_base_rel
* should be called instead.
*/
RelOptInfo *
make_base_rel(Query *root, int relid)
{
RelOptInfo *rel = makeNode(RelOptInfo);
Oid relationObjectId;
rel->relids = makeListi1(relid); rel->relids = makeListi1(relid);
rel->rows = 0; rel->rows = 0;
rel->width = 0; rel->width = 0;
...@@ -95,8 +117,6 @@ get_base_rel(Query *root, int relid) ...@@ -95,8 +117,6 @@ get_base_rel(Query *root, int relid)
rel->issubquery = true; rel->issubquery = true;
} }
root->base_rel_list = lcons(rel, root->base_rel_list);
return rel; return rel;
} }
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.71 2000/11/08 22:09:58 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.72 2000/11/12 00:37:00 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -151,8 +151,12 @@ setTargetTable(ParseState *pstate, char *relname, bool inh, bool inJoinSet) ...@@ -151,8 +151,12 @@ setTargetTable(ParseState *pstate, char *relname, bool inh, bool inJoinSet)
/* /*
* Since the rel was in the rangetable already, it's being read * Since the rel was in the rangetable already, it's being read
* as well as written. Therefore, leave checkForRead true. * as well as written. Therefore, leave checkForRead true.
*
* Force inh to the desired setting for the target (XXX is this
* reasonable? It's *necessary* that INSERT target not be marked
* inheritable, but otherwise not too clear what to do if conflict?)
*/ */
/* XXX what if pre-existing entry has wrong inh setting? */ rte->inh = inh;
} }
/* Mark target table as requiring write access. */ /* Mark target table as requiring write access. */
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/tcop/pquery.c,v 1.39 2000/10/26 21:37:24 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/tcop/pquery.c,v 1.40 2000/11/12 00:37:00 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -68,7 +68,12 @@ CreateExecutorState(void) ...@@ -68,7 +68,12 @@ CreateExecutorState(void)
state->es_direction = ForwardScanDirection; state->es_direction = ForwardScanDirection;
state->es_range_table = NIL; state->es_range_table = NIL;
state->es_result_relations = NULL;
state->es_num_result_relations = 0;
state->es_result_relation_info = NULL; state->es_result_relation_info = NULL;
state->es_junkFilter = NULL;
state->es_into_relation_descriptor = NULL; state->es_into_relation_descriptor = NULL;
state->es_param_list_info = NULL; state->es_param_list_info = NULL;
...@@ -76,8 +81,6 @@ CreateExecutorState(void) ...@@ -76,8 +81,6 @@ CreateExecutorState(void)
state->es_tupleTable = NULL; state->es_tupleTable = NULL;
state->es_junkFilter = NULL;
state->es_query_cxt = CurrentMemoryContext; state->es_query_cxt = CurrentMemoryContext;
state->es_per_tuple_exprcontext = NULL; state->es_per_tuple_exprcontext = NULL;
......
...@@ -37,7 +37,7 @@ ...@@ -37,7 +37,7 @@
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $Id: catversion.h,v 1.58 2000/11/11 19:55:33 thomas Exp $ * $Id: catversion.h,v 1.59 2000/11/12 00:37:00 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -52,7 +52,7 @@ ...@@ -52,7 +52,7 @@
* catalog changes on the same day...) * catalog changes on the same day...)
*/ */
/* yyyymmddN */ /* yyyymmddN */
#define CATALOG_VERSION_NO 200011110 #define CATALOG_VERSION_NO 200011112
#endif #endif
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $Id: executor.h,v 1.52 2000/10/26 21:38:03 tgl Exp $ * $Id: executor.h,v 1.53 2000/11/12 00:37:01 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -56,7 +56,7 @@ extern TupleDesc ExecutorStart(QueryDesc *queryDesc, EState *estate); ...@@ -56,7 +56,7 @@ extern TupleDesc ExecutorStart(QueryDesc *queryDesc, EState *estate);
extern TupleTableSlot *ExecutorRun(QueryDesc *queryDesc, EState *estate, extern TupleTableSlot *ExecutorRun(QueryDesc *queryDesc, EState *estate,
int feature, long count); int feature, long count);
extern void ExecutorEnd(QueryDesc *queryDesc, EState *estate); extern void ExecutorEnd(QueryDesc *queryDesc, EState *estate);
extern void ExecConstraints(char *caller, Relation rel, extern void ExecConstraints(char *caller, ResultRelInfo *resultRelInfo,
TupleTableSlot *slot, EState *estate); TupleTableSlot *slot, EState *estate);
extern TupleTableSlot *EvalPlanQual(EState *estate, Index rti, extern TupleTableSlot *EvalPlanQual(EState *estate, Index rti,
ItemPointer tid); ItemPointer tid);
...@@ -153,8 +153,8 @@ extern void FreeExprContext(ExprContext *econtext); ...@@ -153,8 +153,8 @@ extern void FreeExprContext(ExprContext *econtext);
#define ResetExprContext(econtext) \ #define ResetExprContext(econtext) \
MemoryContextReset((econtext)->ecxt_per_tuple_memory) MemoryContextReset((econtext)->ecxt_per_tuple_memory)
extern void ExecOpenIndices(RelationInfo *resultRelationInfo); extern void ExecOpenIndices(ResultRelInfo *resultRelInfo);
extern void ExecCloseIndices(RelationInfo *resultRelationInfo); extern void ExecCloseIndices(ResultRelInfo *resultRelInfo);
extern void ExecInsertIndexTuples(TupleTableSlot *slot, ItemPointer tupleid, extern void ExecInsertIndexTuples(TupleTableSlot *slot, ItemPointer tupleid,
EState *estate, bool is_update); EState *estate, bool is_update);
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $Id: tuptable.h,v 1.15 2000/01/26 05:58:06 momjian Exp $ * $Id: tuptable.h,v 1.16 2000/11/12 00:37:01 tgl Exp $
* *
* NOTES * NOTES
* The tuple table interface is getting pretty ugly. * The tuple table interface is getting pretty ugly.
...@@ -56,7 +56,6 @@ typedef struct TupleTableSlot ...@@ -56,7 +56,6 @@ typedef struct TupleTableSlot
bool ttc_descIsNew; bool ttc_descIsNew;
TupleDesc ttc_tupleDescriptor; TupleDesc ttc_tupleDescriptor;
Buffer ttc_buffer; Buffer ttc_buffer;
int ttc_whichplan;
} TupleTableSlot; } TupleTableSlot;
/* ---------------- /* ----------------
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $Id: execnodes.h,v 1.52 2000/10/26 21:38:12 tgl Exp $ * $Id: execnodes.h,v 1.53 2000/11/12 00:37:01 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -52,31 +52,6 @@ typedef struct IndexInfo ...@@ -52,31 +52,6 @@ typedef struct IndexInfo
bool ii_Unique; bool ii_Unique;
} IndexInfo; } IndexInfo;
/* ----------------
* RelationInfo information
*
* whenever we update an existing relation, we have to
* update indices on the relation. The RelationInfo class
* is used to hold all the information on result relations,
* including indices.. -cim 10/15/89
*
* RangeTableIndex result relation's range table index
* RelationDesc relation descriptor for result relation
* NumIndices number indices existing on result relation
* IndexRelationDescs array of relation descriptors for indices
* IndexRelationInfo array of key/attr info for indices
* ----------------
*/
typedef struct RelationInfo
{
NodeTag type;
Index ri_RangeTableIndex;
Relation ri_RelationDesc;
int ri_NumIndices;
RelationPtr ri_IndexRelationDescs;
IndexInfo **ri_IndexRelationInfo;
} RelationInfo;
/* ---------------- /* ----------------
* ExprContext * ExprContext
* *
...@@ -116,8 +91,6 @@ typedef struct ExprContext ...@@ -116,8 +91,6 @@ typedef struct ExprContext
/* Values to substitute for Aggref nodes in expression */ /* Values to substitute for Aggref nodes in expression */
Datum *ecxt_aggvalues; /* precomputed values for Aggref nodes */ Datum *ecxt_aggvalues; /* precomputed values for Aggref nodes */
bool *ecxt_aggnulls; /* null flags for Aggref nodes */ bool *ecxt_aggnulls; /* null flags for Aggref nodes */
/* Range table that Vars in expression refer to --- seldom needed */
List *ecxt_range_table;
} ExprContext; } ExprContext;
/* /*
...@@ -210,6 +183,35 @@ typedef struct JunkFilter ...@@ -210,6 +183,35 @@ typedef struct JunkFilter
AttrNumber *jf_cleanMap; AttrNumber *jf_cleanMap;
} JunkFilter; } JunkFilter;
/* ----------------
* ResultRelInfo information
*
* whenever we update an existing relation, we have to
* update indices on the relation. The ResultRelInfo class
* is used to hold all the information on result relations,
* including indices.. -cim 10/15/89
*
* RangeTableIndex result relation's range table index
* RelationDesc relation descriptor for result relation
* NumIndices # of indices existing on result relation
* IndexRelationDescs array of relation descriptors for indices
* IndexRelationInfo array of key/attr info for indices
* ConstraintExprs array of constraint-checking expressions
* junkFilter for removing junk attributes from tuples
* ----------------
*/
typedef struct ResultRelInfo
{
NodeTag type;
Index ri_RangeTableIndex;
Relation ri_RelationDesc;
int ri_NumIndices;
RelationPtr ri_IndexRelationDescs;
IndexInfo **ri_IndexRelationInfo;
List **ri_ConstraintExprs;
JunkFilter *ri_junkFilter;
} ResultRelInfo;
/* ---------------- /* ----------------
* EState information * EState information
* *
...@@ -217,7 +219,7 @@ typedef struct JunkFilter ...@@ -217,7 +219,7 @@ typedef struct JunkFilter
* *
* range_table array of scan relation information * range_table array of scan relation information
* *
* result_relation_information for update queries * result_relation information for insert/update/delete queries
* *
* into_relation_descriptor relation being retrieved "into" * into_relation_descriptor relation being retrieved "into"
* *
...@@ -227,10 +229,6 @@ typedef struct JunkFilter ...@@ -227,10 +229,6 @@ typedef struct JunkFilter
* tupleTable this is a pointer to an array * tupleTable this is a pointer to an array
* of pointers to tuples used by * of pointers to tuples used by
* the executor at any given moment. * the executor at any given moment.
*
* junkFilter contains information used to
* extract junk attributes from a tuple.
* (see JunkFilter above)
* ---------------- * ----------------
*/ */
typedef struct EState typedef struct EState
...@@ -239,23 +237,24 @@ typedef struct EState ...@@ -239,23 +237,24 @@ typedef struct EState
ScanDirection es_direction; ScanDirection es_direction;
Snapshot es_snapshot; Snapshot es_snapshot;
List *es_range_table; List *es_range_table;
RelationInfo *es_result_relation_info; ResultRelInfo *es_result_relations; /* array of ResultRelInfos */
int es_num_result_relations; /* length of array */
ResultRelInfo *es_result_relation_info; /* currently active array elt */
JunkFilter *es_junkFilter; /* currently active junk filter */
Relation es_into_relation_descriptor; Relation es_into_relation_descriptor;
ParamListInfo es_param_list_info; ParamListInfo es_param_list_info;
ParamExecData *es_param_exec_vals; /* this is for subselects */ ParamExecData *es_param_exec_vals; /* this is for subselects */
TupleTable es_tupleTable; TupleTable es_tupleTable;
JunkFilter *es_junkFilter;
uint32 es_processed; /* # of tuples processed */ uint32 es_processed; /* # of tuples processed */
Oid es_lastoid; /* last oid processed (by INSERT) */ Oid es_lastoid; /* last oid processed (by INSERT) */
List *es_rowMark; /* not good place, but there is no other */ List *es_rowMark; /* not good place, but there is no other */
MemoryContext es_query_cxt; /* per-query context in which EState lives */ MemoryContext es_query_cxt; /* per-query context in which EState lives */
/* this ExprContext is for per-output-tuple operations, such as /*
* this ExprContext is for per-output-tuple operations, such as
* constraint checks and index-value computations. It can be reset * constraint checks and index-value computations. It can be reset
* for each output tuple. Note that it will be created only if needed. * for each output tuple. Note that it will be created only if needed.
*/ */
ExprContext *es_per_tuple_exprcontext; ExprContext *es_per_tuple_exprcontext;
/* this field is storage space for ExecConstraints(): */
List **es_result_relation_constraints;
/* Below is to re-evaluate plan qual in READ COMMITTED mode */ /* Below is to re-evaluate plan qual in READ COMMITTED mode */
struct Plan *es_origPlan; struct Plan *es_origPlan;
Pointer es_evalPlanQual; Pointer es_evalPlanQual;
...@@ -341,15 +340,9 @@ typedef struct ResultState ...@@ -341,15 +340,9 @@ typedef struct ResultState
/* ---------------- /* ----------------
* AppendState information * AppendState information
* *
* append nodes have this field "unionplans" which is this * whichplan which plan is being executed (0 .. n-1)
* list of plans to execute in sequence.. these variables
* keep track of things..
*
* whichplan which plan is being executed
* nplans how many plans are in the list * nplans how many plans are in the list
* initialized array of ExecInitNode() results * initialized array of ExecInitNode() results
* result_relation_info_list array of each subplan's result relation info
* junkFilter_list array of each subplan's junk filter
* ---------------- * ----------------
*/ */
typedef struct AppendState typedef struct AppendState
...@@ -358,8 +351,6 @@ typedef struct AppendState ...@@ -358,8 +351,6 @@ typedef struct AppendState
int as_whichplan; int as_whichplan;
int as_nplans; int as_nplans;
bool *as_initialized; bool *as_initialized;
List *as_result_relation_info_list;
List *as_junkFilter_list;
} AppendState; } AppendState;
/* ---------------------------------------------------------------- /* ----------------------------------------------------------------
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $Id: nodes.h,v 1.82 2000/11/05 22:50:21 vadim Exp $ * $Id: nodes.h,v 1.83 2000/11/12 00:37:01 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -83,11 +83,12 @@ typedef enum NodeTag ...@@ -83,11 +83,12 @@ typedef enum NodeTag
T_NestPath, T_NestPath,
T_MergePath, T_MergePath,
T_HashPath, T_HashPath,
T_TidPath,
T_AppendPath,
T_PathKeyItem, T_PathKeyItem,
T_RestrictInfo, T_RestrictInfo,
T_JoinInfo, T_JoinInfo,
T_Stream, T_Stream,
T_TidPath,
T_IndexOptInfo, T_IndexOptInfo,
/*--------------------- /*---------------------
...@@ -95,7 +96,7 @@ typedef enum NodeTag ...@@ -95,7 +96,7 @@ typedef enum NodeTag
*--------------------- *---------------------
*/ */
T_IndexInfo = 300, T_IndexInfo = 300,
T_RelationInfo, T_ResultRelInfo,
T_TupleCount, T_TupleCount,
T_TupleTableSlot, T_TupleTableSlot,
T_ExprContext, T_ExprContext,
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $Id: parsenodes.h,v 1.119 2000/11/05 22:50:21 vadim Exp $ * $Id: parsenodes.h,v 1.120 2000/11/12 00:37:01 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -70,6 +70,16 @@ typedef struct Query ...@@ -70,6 +70,16 @@ typedef struct Query
Node *setOperations; /* set-operation tree if this is top level Node *setOperations; /* set-operation tree if this is top level
* of a UNION/INTERSECT/EXCEPT query */ * of a UNION/INTERSECT/EXCEPT query */
/*
* If the resultRelation turns out to be the parent of an inheritance
* tree, the planner will add all the child tables to the rtable and
* store a list of the rtindexes of all the result relations here.
* This is done at plan time, not parse time, since we don't want to
* commit to the exact set of child tables at parse time. This field
* ought to go in some sort of TopPlan plan node, not in the Query.
*/
List *resultRelations; /* integer list of RT indexes, or NIL */
/* internal to planner */ /* internal to planner */
List *base_rel_list; /* list of base-relation RelOptInfos */ List *base_rel_list; /* list of base-relation RelOptInfos */
List *join_rel_list; /* list of join-relation RelOptInfos */ List *join_rel_list; /* list of join-relation RelOptInfos */
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $Id: plannodes.h,v 1.45 2000/10/26 21:38:12 tgl Exp $ * $Id: plannodes.h,v 1.46 2000/11/12 00:37:01 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -134,8 +134,10 @@ typedef struct Plan ...@@ -134,8 +134,10 @@ typedef struct Plan
/* ---------------- /* ----------------
* result node - * Result node -
* returns tuples from outer plan that satisfy the qualifications * If no outer plan, evaluate a variable-free targetlist.
* If outer plan, return tuples from outer plan that satisfy
* given quals (we can also do a level of projection)
* ---------------- * ----------------
*/ */
typedef struct Result typedef struct Result
...@@ -146,21 +148,21 @@ typedef struct Result ...@@ -146,21 +148,21 @@ typedef struct Result
} Result; } Result;
/* ---------------- /* ----------------
* append node * Append node -
* Generate the concatenation of the results of sub-plans.
* *
* Append nodes can modify the query's rtable during execution. * Append nodes are sometimes used to switch between several result relations
* If inheritrelid > 0, then the RTE with index inheritrelid is replaced * (when the target of an UPDATE or DELETE is an inheritance set). Such a
* by the i'th element of inheritrtable to execute the i'th subplan. * node will have isTarget true. The Append executor is then responsible
* We assume that this RTE is not used in any other part of the * for updating the executor state to point at the correct target relation
* query plan tree, else confusion may result... * whenever it switches subplans.
* ---------------- * ----------------
*/ */
typedef struct Append typedef struct Append
{ {
Plan plan; Plan plan;
List *appendplans; List *appendplans;
Index inheritrelid; bool isTarget;
List *inheritrtable;
AppendState *appendstate; AppendState *appendstate;
} Append; } Append;
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $Id: relation.h,v 1.49 2000/09/29 18:21:39 tgl Exp $ * $Id: relation.h,v 1.50 2000/11/12 00:37:01 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -82,6 +82,14 @@ typedef enum CostSelector ...@@ -82,6 +82,14 @@ typedef enum CostSelector
* upon creation of the RelOptInfo object; they are filled in when * upon creation of the RelOptInfo object; they are filled in when
* set_base_rel_pathlist processes the object. * set_base_rel_pathlist processes the object.
* *
* Note: if a base relation is the root of an inheritance tree
* (SELECT FROM foo*) it is still considered a base rel. We will
* generate a list of candidate Paths for accessing that table itself,
* and also generate baserel RelOptInfo nodes for each child table,
* with their own candidate Path lists. Then, an AppendPath is built
* from the cheapest Path for each of these tables, and set to be the
* only available Path for the inheritance baserel.
*
* * The presence of the remaining fields depends on the restrictions * * The presence of the remaining fields depends on the restrictions
* and joins that the relation participates in: * and joins that the relation participates in:
* *
...@@ -313,6 +321,9 @@ typedef struct IndexPath ...@@ -313,6 +321,9 @@ typedef struct IndexPath
double rows; /* estimated number of result tuples */ double rows; /* estimated number of result tuples */
} IndexPath; } IndexPath;
/*
* TidPath represents a scan by TID
*/
typedef struct TidPath typedef struct TidPath
{ {
Path path; Path path;
...@@ -320,6 +331,17 @@ typedef struct TidPath ...@@ -320,6 +331,17 @@ typedef struct TidPath
Relids unjoined_relids;/* some rels not yet part of my Path */ Relids unjoined_relids;/* some rels not yet part of my Path */
} TidPath; } TidPath;
/*
* AppendPath represents an Append plan, ie, successive execution of
* several member plans. Currently it is only used to handle expansion
* of inheritance trees.
*/
typedef struct AppendPath
{
Path path;
List *subpaths; /* list of component Paths */
} AppendPath;
/* /*
* All join-type paths share these fields. * All join-type paths share these fields.
*/ */
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $Id: pathnode.h,v 1.30 2000/10/05 19:48:33 momjian Exp $ * $Id: pathnode.h,v 1.31 2000/11/12 00:37:01 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -32,6 +32,7 @@ extern IndexPath *create_index_path(Query *root, RelOptInfo *rel, ...@@ -32,6 +32,7 @@ extern IndexPath *create_index_path(Query *root, RelOptInfo *rel,
List *restriction_clauses, List *restriction_clauses,
ScanDirection indexscandir); ScanDirection indexscandir);
extern TidPath *create_tidscan_path(RelOptInfo *rel, List *tideval); extern TidPath *create_tidscan_path(RelOptInfo *rel, List *tideval);
extern AppendPath *create_append_path(RelOptInfo *rel, List *subpaths);
extern Path *create_subqueryscan_path(RelOptInfo *rel); extern Path *create_subqueryscan_path(RelOptInfo *rel);
extern NestPath *create_nestloop_path(RelOptInfo *joinrel, extern NestPath *create_nestloop_path(RelOptInfo *joinrel,
...@@ -63,6 +64,7 @@ extern HashPath *create_hashjoin_path(RelOptInfo *joinrel, ...@@ -63,6 +64,7 @@ extern HashPath *create_hashjoin_path(RelOptInfo *joinrel,
* prototypes for relnode.c * prototypes for relnode.c
*/ */
extern RelOptInfo *get_base_rel(Query *root, int relid); extern RelOptInfo *get_base_rel(Query *root, int relid);
extern RelOptInfo *make_base_rel(Query *root, int relid);
extern RelOptInfo *get_join_rel(Query *root, RelOptInfo *outer_rel, extern RelOptInfo *get_join_rel(Query *root, RelOptInfo *outer_rel,
RelOptInfo *inner_rel, RelOptInfo *inner_rel,
List **restrictlist_ptr); List **restrictlist_ptr);
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $Id: planmain.h,v 1.47 2000/10/26 21:38:24 tgl Exp $ * $Id: planmain.h,v 1.48 2000/11/12 00:37:01 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -28,6 +28,7 @@ extern Plan *query_planner(Query *root, List *tlist, double tuple_fraction); ...@@ -28,6 +28,7 @@ extern Plan *query_planner(Query *root, List *tlist, double tuple_fraction);
extern Plan *create_plan(Query *root, Path *best_path); extern Plan *create_plan(Query *root, Path *best_path);
extern SubqueryScan *make_subqueryscan(List *qptlist, List *qpqual, extern SubqueryScan *make_subqueryscan(List *qptlist, List *qpqual,
Index scanrelid, Plan *subplan); Index scanrelid, Plan *subplan);
extern Append *make_append(List *appendplans, bool isTarget, List *tlist);
extern Sort *make_sort(List *tlist, Plan *lefttree, int keycount); extern Sort *make_sort(List *tlist, Plan *lefttree, int keycount);
extern Sort *make_sort_from_pathkeys(List *tlist, Plan *lefttree, extern Sort *make_sort_from_pathkeys(List *tlist, Plan *lefttree,
List *pathkeys); List *pathkeys);
...@@ -41,7 +42,6 @@ extern Limit *make_limit(List *tlist, Plan *lefttree, ...@@ -41,7 +42,6 @@ extern Limit *make_limit(List *tlist, Plan *lefttree,
extern SetOp *make_setop(SetOpCmd cmd, List *tlist, Plan *lefttree, extern SetOp *make_setop(SetOpCmd cmd, List *tlist, Plan *lefttree,
List *distinctList, AttrNumber flagColIdx); List *distinctList, AttrNumber flagColIdx);
extern Result *make_result(List *tlist, Node *resconstantqual, Plan *subplan); extern Result *make_result(List *tlist, Node *resconstantqual, Plan *subplan);
extern void copy_plan_costsize(Plan *dest, Plan *src);
/* /*
* prototypes for plan/initsplan.c * prototypes for plan/initsplan.c
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $Id: planner.h,v 1.17 2000/10/05 19:11:37 tgl Exp $ * $Id: planner.h,v 1.18 2000/11/12 00:37:01 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -20,7 +20,6 @@ ...@@ -20,7 +20,6 @@
extern Plan *planner(Query *parse); extern Plan *planner(Query *parse);
extern Plan *subquery_planner(Query *parse, double tuple_fraction); extern Plan *subquery_planner(Query *parse, double tuple_fraction);
extern Plan *union_planner(Query *parse, double tuple_fraction);
extern Plan *make_sortplan(List *tlist, Plan *plannode, List *sortcls); extern Plan *make_sortplan(List *tlist, Plan *plannode, List *sortcls);
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $Id: prep.h,v 1.24 2000/10/05 19:11:37 tgl Exp $ * $Id: prep.h,v 1.25 2000/11/12 00:37:01 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -33,10 +33,12 @@ extern List *preprocess_targetlist(List *tlist, int command_type, ...@@ -33,10 +33,12 @@ extern List *preprocess_targetlist(List *tlist, int command_type,
* prototypes for prepunion.c * prototypes for prepunion.c
*/ */
extern Plan *plan_set_operations(Query *parse); extern Plan *plan_set_operations(Query *parse);
extern List *find_all_inheritors(Oid parentrel); extern List *find_all_inheritors(Oid parentrel);
extern bool find_inheritable_rt_entry(List *rangetable, extern List *expand_inherted_rtentry(Query *parse, Index rti);
Index *rt_index, List **inheritors);
extern Plan *plan_inherit_queries(Query *root, List *tlist, extern Node *adjust_inherited_attrs(Node *node,
Index rt_index, List *inheritors); Index old_rt_index, Oid old_relid,
Index new_rt_index, Oid new_relid);
#endif /* PREP_H */ #endif /* PREP_H */
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment