1
0
mirror of https://github.com/postgres/postgres.git synced 2025-11-12 05:01:15 +03:00

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.
This commit is contained in:
Tom Lane
2000-11-12 00:37:02 +00:00
parent 609f9199af
commit 6543d81d65
37 changed files with 1258 additions and 1253 deletions

View File

@@ -8,7 +8,7 @@
*
*
* 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
* The PerformAddAttribute() code, like most of the relation
@@ -1098,13 +1098,12 @@ AlterTableAddConstraint(char *relationName,
case CONSTR_CHECK:
{
ParseState *pstate;
bool successful = TRUE;
bool successful = true;
HeapScanDesc scan;
ExprContext *econtext;
TupleTableSlot *slot = makeNode(TupleTableSlot);
HeapTuple tuple;
RangeTblEntry *rte;
List *rtlist;
List *qual;
List *constlist;
Relation rel;
@@ -1112,9 +1111,9 @@ AlterTableAddConstraint(char *relationName,
char *name;
if (constr->name)
name=constr->name;
name = constr->name;
else
name="<unnamed>";
name = "<unnamed>";
constlist = makeList1(constr);
@@ -1169,13 +1168,6 @@ AlterTableAddConstraint(char *relationName,
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
* for ExecQual, and then call it to evaluate the
@@ -1188,10 +1180,8 @@ AlterTableAddConstraint(char *relationName,
slot->ttc_descIsNew = true;
slot->ttc_tupleDescriptor = rel->rd_att;
slot->ttc_buffer = InvalidBuffer;
slot->ttc_whichplan = -1;
econtext = MakeExprContext(slot, CurrentMemoryContext);
econtext->ecxt_range_table = rtlist; /* range table */
if (!ExecQual(qual, econtext, true))
{
successful=false;
@@ -1201,8 +1191,6 @@ AlterTableAddConstraint(char *relationName,
}
pfree(slot);
pfree(rtlist);
pfree(rte);
heap_endscan(scan);
heap_close(rel, NoLock);

View File

@@ -7,7 +7,7 @@
*
*
* 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,
int32 ntuples,
tuples_read = 0;
bool reading_to_eof = true;
RelationInfo *relationInfo;
ResultRelInfo *resultRelInfo;
EState *estate = CreateExecutorState(); /* for ExecConstraints() */
TupleTable tupleTable;
TupleTableSlot *slot;
@@ -609,20 +609,19 @@ CopyFrom(Relation rel, bool binary, bool oids, FILE *fp,
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
* of code here that basically duplicated execUtils.c ...)
*/
relationInfo = makeNode(RelationInfo);
relationInfo->ri_RangeTableIndex = 1; /* dummy */
relationInfo->ri_RelationDesc = rel;
relationInfo->ri_NumIndices = 0;
relationInfo->ri_IndexRelationDescs = NULL;
relationInfo->ri_IndexRelationInfo = NULL;
resultRelInfo = makeNode(ResultRelInfo);
resultRelInfo->ri_RangeTableIndex = 1; /* dummy */
resultRelInfo->ri_RelationDesc = rel;
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 */
tupleTable = ExecCreateTupleTable(1);
@@ -830,7 +829,7 @@ CopyFrom(Relation rel, bool binary, bool oids, FILE *fp,
ExecStoreTuple(tuple, slot, InvalidBuffer, false);
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
@@ -838,7 +837,7 @@ CopyFrom(Relation rel, bool binary, bool oids, FILE *fp,
*/
heap_insert(rel, tuple);
if (relationInfo->ri_NumIndices > 0)
if (resultRelInfo->ri_NumIndices > 0)
ExecInsertIndexTuples(slot, &(tuple->t_self), estate, false);
/* AFTER ROW INSERT Triggers */
@@ -886,7 +885,7 @@ CopyFrom(Relation rel, bool binary, bool oids, FILE *fp,
ExecDropTupleTable(tupleTable, true);
ExecCloseIndices(relationInfo);
ExecCloseIndices(resultRelInfo);
}

View File

@@ -5,7 +5,7 @@
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* 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)
if (IsA(plan, Append))
{
Append *appendplan = (Append *) plan;
List *saved_rtable = es->rtable;
int whichplan = 0;
List *lst;
foreach(lst, appendplan->appendplans)
{
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++)
appendStringInfo(str, " ");
appendStringInfo(str, " -> ");
explain_outNode(str, subnode, indent + 3, es);
whichplan++;
}
es->rtable = saved_rtable;
}
if (IsA(plan, SubqueryScan))

View File

@@ -27,7 +27,7 @@
*
*
* 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,
Query *parseTree,
Plan *plan,
EState *estate);
static void initResultRelInfo(ResultRelInfo *resultRelInfo,
Index resultRelationIndex,
List *rangeTable,
CmdType operation);
static void EndPlan(Plan *plan, EState *estate);
static TupleTableSlot *ExecutePlan(EState *estate, Plan *plan,
CmdType operation,
@@ -310,10 +314,6 @@ ExecCheckQueryPerms(CmdType operation, Query *parseTree, Plan *plan)
* ExecCheckPlanPerms
* Recursively scan the plan tree to check access permissions in
* 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
ExecCheckPlanPerms(Plan *plan, List *rangeTable, CmdType operation)
@@ -365,27 +365,11 @@ ExecCheckPlanPerms(Plan *plan, List *rangeTable, CmdType operation)
Append *app = (Append *) plan;
List *appendplans;
if (app->inheritrelid > 0)
foreach(appendplans, app->appendplans)
{
/* Append implements expansion of inheritance */
ExecCheckRTPerms(app->inheritrtable, 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);
}
ExecCheckPlanPerms((Plan *) lfirst(appendplans),
rangeTable,
operation);
}
break;
}
@@ -518,10 +502,8 @@ static TupleDesc
InitPlan(CmdType operation, Query *parseTree, Plan *plan, EState *estate)
{
List *rangeTable;
int resultRelation;
Relation intoRelationDesc;
TupleDesc tupType;
List *targetList;
/*
* Do permissions checks.
@@ -532,7 +514,6 @@ InitPlan(CmdType operation, Query *parseTree, Plan *plan, EState *estate)
* get information from query descriptor
*/
rangeTable = parseTree->rtable;
resultRelation = parseTree->resultRelation;
/*
* initialize the node's execution state
@@ -540,63 +521,61 @@ InitPlan(CmdType operation, Query *parseTree, Plan *plan, EState *estate)
estate->es_range_table = rangeTable;
/*
* initialize result relation stuff
* if there is a result relation, initialize result relation stuff
*/
if (resultRelation != 0 && operation != CMD_SELECT)
if (parseTree->resultRelation != 0 && operation != CMD_SELECT)
{
List *resultRelations = parseTree->resultRelations;
int numResultRelations;
ResultRelInfo *resultRelInfos;
/*
* if we have a result relation, open it and initialize the result
* relation info stuff.
*/
RelationInfo *resultRelationInfo;
Index resultRelationIndex;
Oid resultRelationOid;
Relation resultRelationDesc;
if (resultRelations != NIL)
{
/*
* Multiple result relations (due to inheritance)
* parseTree->resultRelations identifies them all
*/
ResultRelInfo *resultRelInfo;
resultRelationIndex = resultRelation;
resultRelationOid = getrelid(resultRelationIndex, rangeTable);
resultRelationDesc = heap_open(resultRelationOid, RowExclusiveLock);
numResultRelations = length(resultRelations);
resultRelInfos = (ResultRelInfo *)
palloc(numResultRelations * sizeof(ResultRelInfo));
resultRelInfo = resultRelInfos;
while (resultRelations != NIL)
{
initResultRelInfo(resultRelInfo,
lfirsti(resultRelations),
rangeTable,
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);
}
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;
/*
* 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(resultRelationInfo);
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
{
/*
* 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;
}
@@ -642,19 +621,17 @@ InitPlan(CmdType operation, Query *parseTree, Plan *plan, EState *estate)
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
* "retrieve into")
*/
tupType = ExecGetTupType(plan); /* tuple descriptor */
targetList = plan->targetlist;
/*
* Now that we have the target list, initialize the junk filter if
* needed. SELECT and INSERT queries need a filter if there are any
* junk attrs in the tlist. UPDATE and DELETE always need one, since
* there's always a junk 'ctid' attribute present --- no need to look
* first.
* Initialize the junk filter if needed. SELECT and INSERT queries need
* a filter if there are any junk attrs in the tlist. UPDATE and
* DELETE always need one, since there's always a junk 'ctid' attribute
* present --- no need to look first.
*/
{
bool junk_filter_needed = false;
@@ -664,7 +641,7 @@ InitPlan(CmdType operation, Query *parseTree, Plan *plan, EState *estate)
{
case CMD_SELECT:
case CMD_INSERT:
foreach(tlist, targetList)
foreach(tlist, plan->targetlist)
{
TargetEntry *tle = (TargetEntry *) lfirst(tlist);
@@ -685,12 +662,55 @@ InitPlan(CmdType operation, Query *parseTree, Plan *plan, EState *estate)
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;
estate->es_junkFilter = j;
/* 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;
if (operation == CMD_SELECT)
tupType = j->jf_cleanTupType;
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;
if (estate->es_result_relation_info)
estate->es_result_relation_info->ri_junkFilter = j;
/* For SELECT, want to return the cleaned tuple type */
if (operation == CMD_SELECT)
tupType = j->jf_cleanTupType;
}
}
else
estate->es_junkFilter = NULL;
@@ -762,6 +782,59 @@ InitPlan(CmdType operation, Query *parseTree, Plan *plan, EState *estate)
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
*
@@ -771,7 +844,8 @@ InitPlan(CmdType operation, Query *parseTree, Plan *plan, EState *estate)
static void
EndPlan(Plan *plan, EState *estate)
{
RelationInfo *resultRelationInfo;
ResultRelInfo *resultRelInfo;
int i;
List *l;
/*
@@ -792,16 +866,16 @@ EndPlan(Plan *plan, EState *estate)
estate->es_tupleTable = NULL;
/*
* close the result relation if necessary, but hold lock on it
* until xact commit. NB: must not do this till after ExecEndNode(),
* see nodeAppend.c ...
* close the result relation(s) if any, but hold locks
* until xact commit.
*/
resultRelationInfo = estate->es_result_relation_info;
if (resultRelationInfo != NULL)
resultRelInfo = estate->es_result_relations;
for (i = estate->es_num_result_relations; i > 0; i--)
{
heap_close(resultRelationInfo->ri_RelationDesc, NoLock);
/* close indices on the result relation, too */
ExecCloseIndices(resultRelationInfo);
/* Close indices and then the relation itself */
ExecCloseIndices(resultRelInfo);
heap_close(resultRelInfo->ri_RelationDesc, NoLock);
resultRelInfo++;
}
/*
@@ -1116,7 +1190,7 @@ ExecAppend(TupleTableSlot *slot,
EState *estate)
{
HeapTuple tuple;
RelationInfo *resultRelationInfo;
ResultRelInfo *resultRelInfo;
Relation resultRelationDesc;
int numIndices;
Oid newId;
@@ -1127,10 +1201,10 @@ ExecAppend(TupleTableSlot *slot,
tuple = slot->val;
/*
* get information on the result relation
* get information on the (current) result relation
*/
resultRelationInfo = estate->es_result_relation_info;
resultRelationDesc = resultRelationInfo->ri_RelationDesc;
resultRelInfo = estate->es_result_relation_info;
resultRelationDesc = resultRelInfo->ri_RelationDesc;
/* BEFORE ROW INSERT Triggers */
if (resultRelationDesc->trigdesc &&
@@ -1154,9 +1228,8 @@ ExecAppend(TupleTableSlot *slot,
/*
* Check the constraints of the tuple
*/
if (resultRelationDesc->rd_att->constr)
ExecConstraints("ExecAppend", resultRelationDesc, slot, estate);
ExecConstraints("ExecAppend", resultRelInfo, slot, estate);
/*
* insert the tuple
@@ -1174,7 +1247,7 @@ ExecAppend(TupleTableSlot *slot,
* the tupleid of the new tuple is placed in the new tuple's t_ctid
* field.
*/
numIndices = resultRelationInfo->ri_NumIndices;
numIndices = resultRelInfo->ri_NumIndices;
if (numIndices > 0)
ExecInsertIndexTuples(slot, &(tuple->t_self), estate, false);
@@ -1195,16 +1268,16 @@ ExecDelete(TupleTableSlot *slot,
ItemPointer tupleid,
EState *estate)
{
RelationInfo *resultRelationInfo;
ResultRelInfo *resultRelInfo;
Relation resultRelationDesc;
ItemPointerData ctid;
int result;
/*
* get the result relation information
* get information on the (current) result relation
*/
resultRelationInfo = estate->es_result_relation_info;
resultRelationDesc = resultRelationInfo->ri_RelationDesc;
resultRelInfo = estate->es_result_relation_info;
resultRelationDesc = resultRelInfo->ri_RelationDesc;
/* BEFORE ROW DELETE Triggers */
if (resultRelationDesc->trigdesc &&
@@ -1237,7 +1310,7 @@ ldelete:;
else if (!(ItemPointerEquals(tupleid, &ctid)))
{
TupleTableSlot *epqslot = EvalPlanQual(estate,
resultRelationInfo->ri_RangeTableIndex, &ctid);
resultRelInfo->ri_RangeTableIndex, &ctid);
if (!TupIsNull(epqslot))
{
@@ -1287,7 +1360,7 @@ ExecReplace(TupleTableSlot *slot,
EState *estate)
{
HeapTuple tuple;
RelationInfo *resultRelationInfo;
ResultRelInfo *resultRelInfo;
Relation resultRelationDesc;
ItemPointerData ctid;
int result;
@@ -1308,10 +1381,10 @@ ExecReplace(TupleTableSlot *slot,
tuple = slot->val;
/*
* get the result relation information
* get information on the (current) result relation
*/
resultRelationInfo = estate->es_result_relation_info;
resultRelationDesc = resultRelationInfo->ri_RelationDesc;
resultRelInfo = estate->es_result_relation_info;
resultRelationDesc = resultRelInfo->ri_RelationDesc;
/* BEFORE ROW UPDATE Triggers */
if (resultRelationDesc->trigdesc &&
@@ -1335,9 +1408,8 @@ ExecReplace(TupleTableSlot *slot,
/*
* Check the constraints of the tuple
*/
if (resultRelationDesc->rd_att->constr)
ExecConstraints("ExecReplace", resultRelationDesc, slot, estate);
ExecConstraints("ExecReplace", resultRelInfo, slot, estate);
/*
* replace the heap tuple
@@ -1358,7 +1430,7 @@ lreplace:;
else if (!(ItemPointerEquals(tupleid, &ctid)))
{
TupleTableSlot *epqslot = EvalPlanQual(estate,
resultRelationInfo->ri_RangeTableIndex, &ctid);
resultRelInfo->ri_RangeTableIndex, &ctid);
if (!TupIsNull(epqslot))
{
@@ -1396,7 +1468,7 @@ lreplace:;
* there.
*/
numIndices = resultRelationInfo->ri_NumIndices;
numIndices = resultRelInfo->ri_NumIndices;
if (numIndices > 0)
ExecInsertIndexTuples(slot, &(tuple->t_self), estate, true);
@@ -1406,8 +1478,10 @@ lreplace:;
}
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;
ConstrCheck *check = rel->rd_att->constr->check;
ExprContext *econtext;
@@ -1415,6 +1489,24 @@ ExecRelCheck(Relation rel, TupleTableSlot *slot, EState *estate)
List *qual;
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
* 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)
else
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 */
econtext->ecxt_scantuple = slot;
/* And evaluate the constraints */
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
@@ -1498,9 +1545,10 @@ ExecRelCheck(Relation rel, TupleTableSlot *slot, EState *estate)
}
void
ExecConstraints(char *caller, Relation rel,
ExecConstraints(char *caller, ResultRelInfo *resultRelInfo,
TupleTableSlot *slot, EState *estate)
{
Relation rel = resultRelInfo->ri_RelationDesc;
HeapTuple tuple = slot->val;
TupleConstr *constr = rel->rd_att->constr;
@@ -1524,7 +1572,7 @@ ExecConstraints(char *caller, Relation rel,
{
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",
caller, failed);
}

View File

@@ -8,7 +8,7 @@
*
*
* 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)
tempSlot->ttc_descIsNew = true;
tempSlot->ttc_tupleDescriptor = (TupleDesc) NULL;
tempSlot->ttc_buffer = InvalidBuffer;
tempSlot->ttc_whichplan = -1;
tup = heap_copytuple(heapTuple);
td = CreateTupleDescCopy(tuple_type);

View File

@@ -15,7 +15,7 @@
*
*
* 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)
slot->ttc_descIsNew = true;
slot->ttc_tupleDescriptor = (TupleDesc) NULL;
slot->ttc_buffer = InvalidBuffer;
slot->ttc_whichplan = -1;
return slot;
}
@@ -675,20 +674,11 @@ NodeGetResultTupleSlot(Plan *node)
case T_Append:
{
Append *n = (Append *) node;
AppendState *appendstate;
List *appendplans;
int whichplan;
Plan *subplan;
AppendState *appendstate = ((Append *) node)->appendstate;
appendstate = n->appendstate;
appendplans = n->appendplans;
whichplan = appendstate->as_whichplan;
subplan = (Plan *) nth(whichplan, appendplans);
slot = NodeGetResultTupleSlot(subplan);
break;
slot = appendstate->cstate.cs_ResultTupleSlot;
}
break;
case T_IndexScan:
{

View File

@@ -8,7 +8,7 @@
*
*
* 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)
econtext->ecxt_param_list_info = estate->es_param_list_info;
econtext->ecxt_aggvalues = NULL;
econtext->ecxt_aggnulls = NULL;
econtext->ecxt_range_table = estate->es_range_table;
commonstate->cs_ExprContext = econtext;
}
@@ -215,7 +214,6 @@ MakeExprContext(TupleTableSlot *slot,
econtext->ecxt_param_list_info = NULL;
econtext->ecxt_aggvalues = NULL;
econtext->ecxt_aggnulls = NULL;
econtext->ecxt_range_table = NIL;
return econtext;
}
@@ -649,10 +647,10 @@ QueryDescGetTypeInfo(QueryDesc *queryDesc)
* ExecOpenIndices
*
* 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
* resultRelationInfo->ri_RelationDesc.
* resultRelInfo->ri_RelationDesc.
*
* 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
@@ -662,9 +660,9 @@ QueryDescGetTypeInfo(QueryDesc *queryDesc)
* ----------------------------------------------------------------
*/
void
ExecOpenIndices(RelationInfo *resultRelationInfo)
ExecOpenIndices(ResultRelInfo *resultRelInfo)
{
Relation resultRelation = resultRelationInfo->ri_RelationDesc;
Relation resultRelation = resultRelInfo->ri_RelationDesc;
List *indexoidlist,
*indexoidscan;
int len,
@@ -672,7 +670,7 @@ ExecOpenIndices(RelationInfo *resultRelationInfo)
RelationPtr relationDescs;
IndexInfo **indexInfoArray;
resultRelationInfo->ri_NumIndices = 0;
resultRelInfo->ri_NumIndices = 0;
/* checks for disabled indexes */
if (! RelationGetForm(resultRelation)->relhasindex)
@@ -697,9 +695,9 @@ ExecOpenIndices(RelationInfo *resultRelationInfo)
relationDescs = (RelationPtr) palloc(len * sizeof(Relation));
indexInfoArray = (IndexInfo **) palloc(len * sizeof(IndexInfo *));
resultRelationInfo->ri_NumIndices = len;
resultRelationInfo->ri_IndexRelationDescs = relationDescs;
resultRelationInfo->ri_IndexRelationInfo = indexInfoArray;
resultRelInfo->ri_NumIndices = len;
resultRelInfo->ri_IndexRelationDescs = relationDescs;
resultRelInfo->ri_IndexRelationInfo = indexInfoArray;
/* ----------------
* For each index, open the index relation and save pg_index info.
@@ -765,18 +763,18 @@ ExecOpenIndices(RelationInfo *resultRelationInfo)
/* ----------------------------------------------------------------
* ExecCloseIndices
*
* Close the index relations stored in resultRelationInfo
* Close the index relations stored in resultRelInfo
* ----------------------------------------------------------------
*/
void
ExecCloseIndices(RelationInfo *resultRelationInfo)
ExecCloseIndices(ResultRelInfo *resultRelInfo)
{
int i;
int numIndices;
RelationPtr relationDescs;
numIndices = resultRelationInfo->ri_NumIndices;
relationDescs = resultRelationInfo->ri_IndexRelationDescs;
numIndices = resultRelInfo->ri_NumIndices;
relationDescs = resultRelInfo->ri_IndexRelationDescs;
for (i = 0; i < numIndices; i++)
{
@@ -817,7 +815,7 @@ ExecInsertIndexTuples(TupleTableSlot *slot,
bool is_update)
{
HeapTuple heapTuple;
RelationInfo *resultRelationInfo;
ResultRelInfo *resultRelInfo;
int i;
int numIndices;
RelationPtr relationDescs;
@@ -833,11 +831,11 @@ ExecInsertIndexTuples(TupleTableSlot *slot,
/*
* Get information from the result relation info structure.
*/
resultRelationInfo = estate->es_result_relation_info;
numIndices = resultRelationInfo->ri_NumIndices;
relationDescs = resultRelationInfo->ri_IndexRelationDescs;
indexInfoArray = resultRelationInfo->ri_IndexRelationInfo;
heapRelation = resultRelationInfo->ri_RelationDesc;
resultRelInfo = estate->es_result_relation_info;
numIndices = resultRelInfo->ri_NumIndices;
relationDescs = resultRelInfo->ri_IndexRelationDescs;
indexInfoArray = resultRelInfo->ri_IndexRelationInfo;
heapRelation = resultRelInfo->ri_RelationDesc;
heapDescriptor = RelationGetDescr(heapRelation);
/*

View File

@@ -8,7 +8,7 @@
*
*
* 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)
slot->ttc_descIsNew = true;
slot->ttc_tupleDescriptor = (TupleDesc) NULL;
slot->ttc_buffer = InvalidBuffer;
slot->ttc_whichplan = -1;
fcache->funcSlot = slot;
}

View File

@@ -8,7 +8,7 @@
*
*
* 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 @@
* nil nil ... ... ...
* subplans
*
* Append nodes are currently used for unions, and to support inheritance
* queries, where several relations need to be scanned.
* Append nodes are currently used for unions, and to support
* inheritance queries, where several relations need to be scanned.
* For example, in our standard person/student/employee/student-emp
* example, where student and employee inherit from person
* and student-emp inherits from student and employee, the
@@ -54,8 +54,8 @@
* | | | |
* person employee student student-emp
*/
#include "postgres.h"
#include "postgres.h"
#include "access/heapam.h"
#include "executor/execdebug.h"
@@ -78,12 +78,8 @@ exec_append_initialize_next(Append *node)
{
EState *estate;
AppendState *appendstate;
TupleTableSlot *result_slot;
List *rangeTable;
int whichplan;
int nplans;
List *inheritrtable;
RangeTblEntry *rtentry;
/* ----------------
* get information from the append node
@@ -91,12 +87,8 @@ exec_append_initialize_next(Append *node)
*/
estate = node->plan.state;
appendstate = node->appendstate;
result_slot = appendstate->cstate.cs_ResultTupleSlot;
rangeTable = estate->es_range_table;
whichplan = appendstate->as_whichplan;
nplans = appendstate->as_nplans;
inheritrtable = node->inheritrtable;
if (whichplan < 0)
{
@@ -110,7 +102,6 @@ exec_append_initialize_next(Append *node)
*/
appendstate->as_whichplan = 0;
return FALSE;
}
else if (whichplan >= nplans)
{
@@ -121,37 +112,25 @@ exec_append_initialize_next(Append *node)
*/
appendstate->as_whichplan = nplans - 1;
return FALSE;
}
else
{
/* ----------------
* initialize the scan
* (and update the range table appropriately)
*
* (doesn't this leave the range table hosed for anybody upstream
* of the Append node??? - jolly )
* If we are controlling the target relation, select the proper
* active ResultRelInfo and junk filter for this target.
* ----------------
*/
if (node->inheritrelid > 0)
if (node->isTarget)
{
rtentry = nth(whichplan, inheritrtable);
Assert(rtentry != NULL);
rt_store(node->inheritrelid, rangeTable, rtentry);
Assert(whichplan < estate->es_num_result_relations);
estate->es_result_relation_info =
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;
}
}
@@ -176,14 +155,10 @@ ExecInitAppend(Append *node, EState *estate, Plan *parent)
{
AppendState *appendstate;
int nplans;
List *inheritrtable;
List *appendplans;
bool *initialized;
int i;
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);
@@ -196,7 +171,6 @@ ExecInitAppend(Append *node, EState *estate, Plan *parent)
appendplans = node->appendplans;
nplans = length(appendplans);
inheritrtable = node->inheritrtable;
initialized = (bool *) palloc(nplans * sizeof(bool));
MemSet(initialized, 0, nplans * sizeof(bool));
@@ -228,119 +202,25 @@ ExecInitAppend(Append *node, EState *estate, Plan *parent)
*/
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
* and save the results into the array "initialized"
* ----------------
*/
junkList = NIL;
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;
exec_append_initialize_next(node);
initNode = (Plan *) nth(i, appendplans);
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);
ExecAssignResultType(&appendstate->cstate, ExecGetTupType(initNode));
ExecAssignResultTypeFromTL((Plan *) node, &appendstate->cstate);
appendstate->cstate.cs_ProjInfo = NULL;
/* ----------------
@@ -357,10 +237,9 @@ int
ExecCountSlotsAppend(Append *node)
{
List *plan;
List *appendplans = node->appendplans;
int nSlots = 0;
foreach(plan, appendplans)
foreach(plan, node->appendplans)
nSlots += ExecCountSlotsNode((Plan *) lfirst(plan));
return nSlots + APPEND_NSLOTS;
}
@@ -430,16 +309,14 @@ ExecProcAppend(Append *node)
* direction and try processing again (recursively)
* ----------------
*/
whichplan = appendstate->as_whichplan;
if (ScanDirectionIsForward(direction))
appendstate->as_whichplan = whichplan + 1;
appendstate->as_whichplan++;
else
appendstate->as_whichplan = whichplan - 1;
appendstate->as_whichplan--;
/* ----------------
* 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))
@@ -469,7 +346,6 @@ ExecEndAppend(Append *node)
List *appendplans;
bool *initialized;
int i;
List *resultRelationInfoList;
/* ----------------
* get information from the node
@@ -490,40 +366,8 @@ ExecEndAppend(Append *node)
if (initialized[i])
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
ExecReScanAppend(Append *node, ExprContext *exprCtxt, Plan *parent)
{

View File

@@ -8,7 +8,7 @@
*
*
* 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,
ScanDirection direction;
Relation currentRelation;
HeapScanDesc currentScanDesc;
RelationInfo *resultRelationInfo;
/* ----------------
* get the relation object id from the relid'th entry
@@ -169,7 +168,6 @@ InitScanRelation(SeqScan *node, EState *estate,
rtentry = rt_fetch(relid, rangeTable);
reloid = rtentry->relid;
direction = estate->es_direction;
resultRelationInfo = estate->es_result_relation_info;
ExecOpenScanR(reloid, /* relation */
0, /* nkeys */

View File

@@ -15,7 +15,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* 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)
* ----------------
*/
Node_Copy(from, newnode, appendplans);
newnode->inheritrelid = from->inheritrelid;
Node_Copy(from, newnode, inheritrtable);
newnode->isTarget = from->isTarget;
return newnode;
}
@@ -1275,6 +1274,30 @@ _copyTidPath(TidPath *from)
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
*
@@ -1767,6 +1790,8 @@ _copyQuery(Query *from)
Node_Copy(from, newnode, setOperations);
newnode->resultRelations = listCopy(from->resultRelations);
/*
* We do not copy the planner internal fields: base_rel_list,
* join_rel_list, equi_key_list, query_pathkeys. Not entirely clear if
@@ -2677,6 +2702,9 @@ copyObject(void *from)
case T_TidPath:
retval = _copyTidPath(from);
break;
case T_AppendPath:
retval = _copyAppendPath(from);
break;
case T_NestPath:
retval = _copyNestPath(from);
break;

View File

@@ -20,7 +20,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* 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)
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
_equalJoinPath(JoinPath *a, JoinPath *b)
{
@@ -555,28 +565,6 @@ _equalStream(Stream *a, Stream *b)
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
*/
@@ -624,6 +612,8 @@ _equalQuery(Query *a, Query *b)
return false;
if (!equal(a->setOperations, b->setOperations))
return false;
if (!equali(a->resultRelations, b->resultRelations))
return false;
/*
* We do not check the internal-to-the-planner fields: base_rel_list,
@@ -1851,14 +1841,13 @@ equal(void *a, void *b)
case T_TidPath:
retval = _equalTidPath(a, b);
break;
case T_AppendPath:
retval = _equalAppendPath(a, b);
break;
case T_IndexOptInfo:
retval = _equalIndexOptInfo(a, b);
break;
case T_EState:
retval = _equalEState(a, b);
break;
case T_List:
{
List *la = (List *) a;

View File

@@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* 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
* Every (plan) node in POSTGRES has an associated "out" routine which
@@ -296,6 +296,9 @@ _outQuery(StringInfo str, Query *node)
appendStringInfo(str, " :setOperations ");
_outNode(str, node->setOperations);
appendStringInfo(str, " :resultRelations ");
_outIntList(str, node->resultRelations);
}
static void
@@ -327,17 +330,18 @@ _outSetOperationStmt(StringInfo str, SetOperationStmt *node)
/*
* print the basic stuff of all nodes that inherit from Plan
*
* NOTE: we deliberately omit the execution state (EState)
*/
static void
_outPlanInfo(StringInfo str, Plan *node)
{
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->total_cost,
node->plan_rows,
node->plan_width,
node->state ? "not-NULL" : "<>");
node->plan_width);
_outNode(str, node->targetlist);
appendStringInfo(str, " :qpqual ");
@@ -394,9 +398,8 @@ _outAppend(StringInfo str, Append *node)
appendStringInfo(str, " :appendplans ");
_outNode(str, node->appendplans);
appendStringInfo(str, " :inheritrelid %u :inheritrtable ",
node->inheritrelid);
_outNode(str, node->inheritrtable);
appendStringInfo(str, " :isTarget %s ",
node->isTarget ? "true" : "false");
}
/*
@@ -945,25 +948,6 @@ _outJoinExpr(StringInfo str, JoinExpr *node)
_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
*/
@@ -1107,10 +1091,27 @@ _outTidPath(StringInfo str, TidPath *node)
appendStringInfo(str, " :tideval ");
_outNode(str, node->tideval);
appendStringInfo(str, " :un joined_relids ");
appendStringInfo(str, " :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
*/
@@ -1632,9 +1633,6 @@ _outNode(StringInfo str, void *obj)
case T_JoinExpr:
_outJoinExpr(str, obj);
break;
case T_EState:
_outEState(str, obj);
break;
case T_RelOptInfo:
_outRelOptInfo(str, obj);
break;
@@ -1656,6 +1654,9 @@ _outNode(StringInfo str, void *obj)
case T_TidPath:
_outTidPath(str, obj);
break;
case T_AppendPath:
_outAppendPath(str, obj);
break;
case T_NestPath:
_outNestPath(str, obj);
break;

View File

@@ -8,7 +8,7 @@
*
*
* 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
* Most of the read functions for plan nodes are tested. (In fact, they
@@ -150,6 +150,9 @@ _readQuery(void)
token = lsptok(NULL, &length); /* skip :setOperations */
local_node->setOperations = nodeRead(true);
token = lsptok(NULL, &length); /* skip :resultRelations */
local_node->resultRelations = toIntList(nodeRead(true));
return local_node;
}
@@ -260,17 +263,6 @@ _getPlan(Plan *node)
token = lsptok(NULL, &length); /* get the plan_width */
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 */
node->targetlist = nodeRead(true);
@@ -283,6 +275,8 @@ _getPlan(Plan *node)
token = lsptok(NULL, &length); /* eat :righttree */
node->righttree = (Plan *) nodeRead(true);
node->state = (EState *) NULL; /* never read in */
return;
}
@@ -348,12 +342,9 @@ _readAppend(void)
token = lsptok(NULL, &length); /* eat :appendplans */
local_node->appendplans = nodeRead(true); /* now read it */
token = lsptok(NULL, &length); /* eat :inheritrelid */
token = lsptok(NULL, &length); /* get inheritrelid */
local_node->inheritrelid = strtoul(token, NULL, 10);
token = lsptok(NULL, &length); /* eat :inheritrtable */
local_node->inheritrtable = nodeRead(true); /* now read it */
token = lsptok(NULL, &length); /* eat :isTarget */
token = lsptok(NULL, &length); /* get isTarget */
local_node->isTarget = (token[0] == 't') ? true : false;
return local_node;
}
@@ -1297,43 +1288,6 @@ _readJoinExpr(void)
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
*/
@@ -1639,6 +1593,42 @@ _readTidPath(void)
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
*
@@ -1984,8 +1974,6 @@ parsePlanString(void)
return_value = _readOper();
else if (length == 5 && strncmp(token, "PARAM", length) == 0)
return_value = _readParam();
else if (length == 6 && strncmp(token, "ESTATE", length) == 0)
return_value = _readEState();
else if (length == 10 && strncmp(token, "RELOPTINFO", length) == 0)
return_value = _readRelOptInfo();
else if (length == 11 && strncmp(token, "TARGETENTRY", length) == 0)
@@ -1998,6 +1986,8 @@ parsePlanString(void)
return_value = _readIndexPath();
else if (length == 7 && strncmp(token, "TIDPATH", length) == 0)
return_value = _readTidPath();
else if (length == 10 && strncmp(token, "APPENDPATH", length) == 0)
return_value = _readAppendPath();
else if (length == 8 && strncmp(token, "NESTPATH", length) == 0)
return_value = _readNestPath();
else if (length == 9 && strncmp(token, "MERGEPATH", length) == 0)

View File

@@ -4,11 +4,11 @@ Summary
These directories take the Query structure returned by the parser, and
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
tables, and /prep handles special cases like inheritance. /util is utility
stuff. /geqo is the separate "genetic optimization" planner --- it does
a semi-random search through the join tree space, rather than exhaustively
considering all possible join trees. (But each join considered by /geqo
is given to /path to create paths for, so we consider all possible
tables, and /prep handles various preprocessing steps for special cases.
/util is utility stuff. /geqo is the separate "genetic optimization" planner
--- it does a semi-random search through the join tree space, rather than
exhaustively considering all possible join trees. (But each join considered
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.)
@@ -210,10 +210,10 @@ planner()
thereby reducing the accuracy of selectivity estimates.
process sublinks
convert Vars of outer query levels into Params
--union_planner()
handle unions and inheritance by mutual recursion with prepunion.c routines
preprocess target list
handle GROUP BY, HAVING, aggregates, ORDER BY, DISTINCT
--grouping_planner()
preprocess target list for non-SELECT queries
handle UNION/INTERSECT/EXCEPT, GROUP BY, HAVING, aggregates,
ORDER BY, DISTINCT, LIMIT
--query_planner()
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
@@ -239,11 +239,12 @@ planner()
Loop back if this wasn't the top join level.
Back at query_planner:
put back constant quals and non-simplified target list
Back at union_planner:
Back at grouping_planner:
do grouping(GROUP)
do aggregates
make unique(DISTINCT)
make sort(ORDER BY)
make limit(LIMIT/OFFSET)
Optimizer Data Structures

View File

@@ -8,7 +8,7 @@
*
*
* 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 @@
#include "optimizer/paths.h"
#include "optimizer/plancat.h"
#include "optimizer/planner.h"
#include "optimizer/prep.h"
#include "parser/parsetree.h"
@@ -28,7 +29,12 @@ bool enable_geqo = true;
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,
List *initial_rels);
@@ -51,7 +57,7 @@ make_one_rel(Query *root)
/*
* 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.
@@ -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.
* Sequential scan and any available indices are considered.
* Each useful path is attached to its relation's 'pathlist' field.
*/
static void
set_base_rel_pathlist(Query *root)
set_base_rel_pathlists(Query *root)
{
List *rellist;
foreach(rellist, root->base_rel_list)
{
RelOptInfo *rel = (RelOptInfo *) lfirst(rellist);
Index rti;
RangeTblEntry *rte;
List *inheritlist;
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)
{
@@ -109,47 +118,163 @@ set_base_rel_pathlist(Query *root)
/* Generate appropriate path */
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
{
/* Plain relation */
List *indices = find_secondary_indexes(rte->relid);
/* Mark rel with estimated output rows, width, etc */
set_baserel_size_estimates(root, 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 sequential scan */
add_path(rel, create_seqscan_path(rel));
/* Consider TID scans */
create_tidscan_paths(root, rel);
/* Consider index paths for both simple and OR index clauses */
create_index_paths(root, rel, indices,
rel->baserestrictinfo,
rel->joininfo);
/*
* 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);
set_plain_rel_pathlist(root, rel, rte);
}
/* Now find the cheapest of the paths for this rel */
set_cheapest(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 */
set_baserel_size_estimates(root, 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 sequential scan */
add_path(rel, create_seqscan_path(rel));
/* Consider TID scans */
create_tidscan_paths(root, rel);
/* Consider index paths for both simple and OR index clauses */
create_index_paths(root, rel, indices,
rel->baserestrictinfo,
rel->joininfo);
/*
* 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");
/*
* 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
* Build access paths for a FromExpr jointree node.

View File

@@ -11,7 +11,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* 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,
* NB: the result is NOT in canonical form, but must be passed through
* canonicalize_pathkeys() before it can be used for comparisons or
* labeling relation sort orders. (We do things this way because
* union_planner needs to be able to construct requested pathkeys before
* the pathkey equivalence sets have been created for the query.)
* grouping_planner needs to be able to construct requested pathkeys
* before the pathkey equivalence sets have been created for the query.)
*
* 'sortclauses' is a list of SortClause or GroupClause nodes
* 'tlist' is the targetlist to find the referenced tlist entries in

View File

@@ -10,7 +10,7 @@
*
*
* 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 @@
#include "utils/syscache.h"
static List *switch_outer(List *clauses);
static Scan *create_scan_node(Query *root, Path *best_path, List *tlist);
static Join *create_join_node(Query *root, JoinPath *best_path, List *tlist);
static SeqScan *create_seqscan_node(Path *best_path, List *tlist,
static Scan *create_scan_plan(Query *root, Path *best_path);
static Join *create_join_plan(Query *root, JoinPath *best_path);
static Append *create_append_plan(Query *root, AppendPath *best_path);
static SeqScan *create_seqscan_plan(Path *best_path, List *tlist,
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);
static TidScan *create_tidscan_node(TidPath *best_path, List *tlist,
static TidScan *create_tidscan_plan(TidPath *best_path, List *tlist,
List *scan_clauses);
static SubqueryScan *create_subqueryscan_node(Path *best_path,
static SubqueryScan *create_subqueryscan_plan(Path *best_path,
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,
Plan *outer_node, List *outer_tlist,
Plan *inner_node, List *inner_tlist);
static MergeJoin *create_mergejoin_node(MergePath *best_path, List *tlist,
Plan *outer_plan, List *outer_tlist,
Plan *inner_plan, List *inner_tlist);
static MergeJoin *create_mergejoin_plan(MergePath *best_path, List *tlist,
List *joinclauses, List *otherclauses,
Plan *outer_node, List *outer_tlist,
Plan *inner_node, List *inner_tlist);
static HashJoin *create_hashjoin_node(HashPath *best_path, List *tlist,
Plan *outer_plan, List *outer_tlist,
Plan *inner_plan, List *inner_tlist);
static HashJoin *create_hashjoin_plan(HashPath *best_path, List *tlist,
List *joinclauses, List *otherclauses,
Plan *outer_node, List *outer_tlist,
Plan *inner_node, List *inner_tlist);
Plan *outer_plan, List *outer_tlist,
Plan *inner_plan, List *inner_tlist);
static List *fix_indxqual_references(List *indexquals, IndexPath *index_path);
static List *fix_indxqual_sublist(List *indexqual, int baserelid, Oid relam,
Form_pg_index index);
static Node *fix_indxqual_operand(Node *node, int baserelid,
Form_pg_index index,
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 IndexScan *make_indexscan(List *qptlist, List *qpqual, Index scanrelid,
List *indxid, List *indxqual,
@@ -83,7 +86,6 @@ static MergeJoin *make_mergejoin(List *tlist,
List *mergeclauses,
Plan *lefttree, Plan *righttree,
JoinType jointype);
static void copy_path_costsize(Plan *dest, Path *src);
/*
* create_plan
@@ -98,13 +100,12 @@ static void copy_path_costsize(Plan *dest, Path *src);
*
* best_path is the best access path
*
* Returns the access plan.
* Returns a Plan tree.
*/
Plan *
create_plan(Query *root, Path *best_path)
{
List *tlist = best_path->parent->targetlist;
Plan *plan_node = (Plan *) NULL;
Plan *plan;
switch (best_path->pathtype)
{
@@ -112,18 +113,22 @@ create_plan(Query *root, Path *best_path)
case T_SeqScan:
case T_TidScan:
case T_SubqueryScan:
plan_node = (Plan *) create_scan_node(root, best_path, tlist);
plan = (Plan *) create_scan_plan(root, best_path);
break;
case T_HashJoin:
case T_MergeJoin:
case T_NestLoop:
plan_node = (Plan *) create_join_node(root,
(JoinPath *) best_path,
tlist);
plan = (Plan *) create_join_plan(root,
(JoinPath *) best_path);
break;
case T_Append:
plan = (Plan *) create_append_plan(root,
(AppendPath *) best_path);
break;
default:
elog(ERROR, "create_plan: unknown pathtype %d",
best_path->pathtype);
plan = NULL; /* keep compiler quiet */
break;
}
@@ -131,30 +136,29 @@ create_plan(Query *root, Path *best_path)
/* sort clauses by cost/(1-selectivity) -- JMH 2/26/92 */
if (XfuncMode != XFUNC_OFF)
{
set_qpqual((Plan) plan_node,
lisp_qsort(get_qpqual((Plan) plan_node),
set_qpqual((Plan) plan,
lisp_qsort(get_qpqual((Plan) plan),
xfunc_clause_compare));
if (XfuncMode != XFUNC_NOR)
/* sort the disjuncts within each clause by cost -- JMH 3/4/92 */
xfunc_disjunct_sort(plan_node->qpqual);
xfunc_disjunct_sort(plan->qpqual);
}
#endif
return plan_node;
return plan;
}
/*
* create_scan_node
* Create a scan path for the parent relation of 'best_path'.
* create_scan_plan
* Create a scan plan for the parent relation of 'best_path'.
*
* tlist is the targetlist for the base relation scanned by 'best_path'
*
* Returns the scan node.
* Returns a Plan node.
*/
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;
/*
@@ -166,65 +170,64 @@ create_scan_node(Query *root, Path *best_path, List *tlist)
switch (best_path->pathtype)
{
case T_SeqScan:
node = (Scan *) create_seqscan_node(best_path,
plan = (Scan *) create_seqscan_plan(best_path,
tlist,
scan_clauses);
break;
case T_IndexScan:
node = (Scan *) create_indexscan_node(root,
plan = (Scan *) create_indexscan_plan(root,
(IndexPath *) best_path,
tlist,
scan_clauses);
break;
case T_TidScan:
node = (Scan *) create_tidscan_node((TidPath *) best_path,
plan = (Scan *) create_tidscan_plan((TidPath *) best_path,
tlist,
scan_clauses);
break;
case T_SubqueryScan:
node = (Scan *) create_subqueryscan_node(best_path,
plan = (Scan *) create_subqueryscan_plan(best_path,
tlist,
scan_clauses);
break;
default:
elog(ERROR, "create_scan_node: unknown node type: %d",
elog(ERROR, "create_scan_plan: unknown node type: %d",
best_path->pathtype);
plan = NULL; /* keep compiler quiet */
break;
}
return node;
return plan;
}
/*
* create_join_node
* Create a join path for 'best_path' and(recursively) paths for its
* create_join_plan
* Create a join plan for 'best_path' and (recursively) plans for its
* inner and outer paths.
*
* 'tlist' is the targetlist for the join relation corresponding to
* 'best_path'
*
* Returns the join node.
* Returns a Plan node.
*/
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;
Plan *inner_node;
Plan *inner_plan;
List *inner_tlist;
List *joinclauses;
List *otherclauses;
Join *retval = NULL;
Join *plan;
outer_node = create_plan(root, best_path->outerjoinpath);
outer_tlist = outer_node->targetlist;
outer_plan = create_plan(root, best_path->outerjoinpath);
outer_tlist = outer_plan->targetlist;
inner_node = create_plan(root, best_path->innerjoinpath);
inner_tlist = inner_node->targetlist;
inner_plan = create_plan(root, best_path->innerjoinpath);
inner_tlist = inner_plan->targetlist;
if (IS_OUTER_JOIN(best_path->jointype))
{
@@ -241,38 +244,40 @@ create_join_node(Query *root, JoinPath *best_path, List *tlist)
switch (best_path->path.pathtype)
{
case T_MergeJoin:
retval = (Join *) create_mergejoin_node((MergePath *) best_path,
tlist,
joinclauses,
otherclauses,
outer_node,
outer_tlist,
inner_node,
inner_tlist);
plan = (Join *) create_mergejoin_plan((MergePath *) best_path,
join_tlist,
joinclauses,
otherclauses,
outer_plan,
outer_tlist,
inner_plan,
inner_tlist);
break;
case T_HashJoin:
retval = (Join *) create_hashjoin_node((HashPath *) best_path,
tlist,
joinclauses,
otherclauses,
outer_node,
outer_tlist,
inner_node,
inner_tlist);
plan = (Join *) create_hashjoin_plan((HashPath *) best_path,
join_tlist,
joinclauses,
otherclauses,
outer_plan,
outer_tlist,
inner_plan,
inner_tlist);
break;
case T_NestLoop:
retval = (Join *) create_nestloop_node((NestPath *) best_path,
tlist,
joinclauses,
otherclauses,
outer_node,
outer_tlist,
inner_node,
inner_tlist);
plan = (Join *) create_nestloop_plan((NestPath *) best_path,
join_tlist,
joinclauses,
otherclauses,
outer_plan,
outer_tlist,
inner_plan,
inner_tlist);
break;
default:
elog(ERROR, "create_join_node: unknown node type: %d",
elog(ERROR, "create_join_plan: unknown node type: %d",
best_path->path.pathtype);
plan = NULL; /* keep compiler quiet */
break;
}
#ifdef NOT_USED
@@ -283,14 +288,42 @@ create_join_node(Query *root, JoinPath *best_path, List *tlist)
* JMH, 6/15/92
*/
if (get_loc_restrictinfo(best_path) != NIL)
set_qpqual((Plan) retval,
nconc(get_qpqual((Plan) retval),
set_qpqual((Plan) plan,
nconc(get_qpqual((Plan) plan),
get_actual_clauses(get_loc_restrictinfo(best_path))));
#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
@@ -299,14 +332,14 @@ create_join_node(Query *root, JoinPath *best_path, List *tlist)
/*
* create_seqscan_node
* Returns a seqscan node for the base relation scanned by 'best_path'
* create_seqscan_plan
* Returns a seqscan plan for the base relation scanned by 'best_path'
* with restriction clauses 'scan_clauses' and targetlist 'tlist'.
*/
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;
/* there should be exactly one base rel involved... */
@@ -315,18 +348,18 @@ create_seqscan_node(Path *best_path, List *tlist, List *scan_clauses)
scan_relid = (Index) lfirsti(best_path->parent->relids);
scan_node = make_seqscan(tlist,
scan_plan = make_seqscan(tlist,
scan_clauses,
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
* Returns a indexscan node for the base relation scanned by 'best_path'
* create_indexscan_plan
* Returns a indexscan plan for the base relation scanned by 'best_path'
* with restriction clauses 'scan_clauses' and targetlist 'tlist'.
*
* 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)
* scan.
*/
static IndexScan *
create_indexscan_node(Query *root,
create_indexscan_plan(Query *root,
IndexPath *best_path,
List *tlist,
List *scan_clauses)
@@ -348,7 +381,7 @@ create_indexscan_node(Query *root,
List *qpqual;
List *fixed_indxqual;
List *ixid;
IndexScan *scan_node;
IndexScan *scan_plan;
bool lossy = false;
/* there should be exactly one base rel involved... */
@@ -433,7 +466,7 @@ create_indexscan_node(Query *root,
*/
fixed_indxqual = fix_indxqual_references(indxqual, best_path);
scan_node = make_indexscan(tlist,
scan_plan = make_indexscan(tlist,
qpqual,
baserelid,
best_path->indexid,
@@ -441,22 +474,22 @@ create_indexscan_node(Query *root,
indxqual,
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 */
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
* Returns a tidscan node for the base relation scanned by 'best_path'
* create_tidscan_plan
* Returns a tidscan plan for the base relation scanned by 'best_path'
* with restriction clauses 'scan_clauses' and targetlist 'tlist'.
*/
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;
/* there should be exactly one base rel involved... */
@@ -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_node = make_tidscan(tlist,
scan_plan = make_tidscan(tlist,
scan_clauses,
scan_relid,
best_path->tideval);
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
* Returns a subqueryscan node for the base relation scanned by 'best_path'
* create_subqueryscan_plan
* Returns a subqueryscan plan for the base relation scanned by 'best_path'
* with restriction clauses 'scan_clauses' and targetlist 'tlist'.
*/
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;
/* there should be exactly one base rel involved... */
@@ -496,14 +529,12 @@ create_subqueryscan_node(Path *best_path, List *tlist, List *scan_clauses)
scan_relid = (Index) lfirsti(best_path->parent->relids);
scan_node = make_subqueryscan(tlist,
scan_plan = make_subqueryscan(tlist,
scan_clauses,
scan_relid,
best_path->parent->subplan);
copy_path_costsize(&scan_node->scan.plan, best_path);
return scan_node;
return scan_plan;
}
/*****************************************************************************
@@ -528,18 +559,18 @@ create_subqueryscan_node(Path *best_path, List *tlist, List *scan_clauses)
*****************************************************************************/
static NestLoop *
create_nestloop_node(NestPath *best_path,
create_nestloop_plan(NestPath *best_path,
List *tlist,
List *joinclauses,
List *otherclauses,
Plan *outer_node,
Plan *outer_plan,
List *outer_tlist,
Plan *inner_node,
Plan *inner_plan,
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,
* and therefore has not itself done join_references renumbering
* of the vars in its quals.
*/
IndexScan *innerscan = (IndexScan *) inner_node;
IndexScan *innerscan = (IndexScan *) inner_plan;
List *indxqualorig = innerscan->indxqualorig;
/* No work needed if indxqual refers only to its own relation... */
@@ -591,23 +622,23 @@ create_nestloop_node(NestPath *best_path,
NIL,
innerrel);
/* fix the inner qpqual too, if it has join clauses */
if (NumRelids((Node *) inner_node->qual) > 1)
inner_node->qual = join_references(inner_node->qual,
if (NumRelids((Node *) inner_plan->qual) > 1)
inner_plan->qual = join_references(inner_plan->qual,
outer_tlist,
NIL,
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,
outer_tlist,
inner_tlist,
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,
* join --- how can we estimate whether this is a good thing to
* do?
*/
inner_node = (Plan *) make_material(inner_tlist,
inner_node);
inner_plan = (Plan *) make_material(inner_tlist,
inner_plan);
}
/*
@@ -633,30 +664,30 @@ create_nestloop_node(NestPath *best_path,
inner_tlist,
(Index) 0);
join_node = make_nestloop(tlist,
join_plan = make_nestloop(tlist,
joinclauses,
otherclauses,
outer_node,
inner_node,
outer_plan,
inner_plan,
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 *
create_mergejoin_node(MergePath *best_path,
create_mergejoin_plan(MergePath *best_path,
List *tlist,
List *joinclauses,
List *otherclauses,
Plan *outer_node,
Plan *outer_plan,
List *outer_tlist,
Plan *inner_node,
Plan *inner_plan,
List *inner_tlist)
{
List *mergeclauses;
MergeJoin *join_node;
MergeJoin *join_plan;
mergeclauses = get_actual_clauses(best_path->path_mergeclauses);
@@ -692,15 +723,15 @@ create_mergejoin_node(MergePath *best_path,
* necessary. The sort cost was already accounted for in the path.
*/
if (best_path->outersortkeys)
outer_node = (Plan *)
outer_plan = (Plan *)
make_sort_from_pathkeys(outer_tlist,
outer_node,
outer_plan,
best_path->outersortkeys);
if (best_path->innersortkeys)
inner_node = (Plan *)
inner_plan = (Plan *)
make_sort_from_pathkeys(inner_tlist,
inner_node,
inner_plan,
best_path->innersortkeys);
/*
@@ -723,7 +754,7 @@ create_mergejoin_node(MergePath *best_path,
* This check must agree with ExecMarkPos/ExecRestrPos in
* executor/execAmi.c!
*/
switch (nodeTag(inner_node))
switch (nodeTag(inner_plan))
{
case T_SeqScan:
case T_IndexScan:
@@ -734,40 +765,40 @@ create_mergejoin_node(MergePath *best_path,
default:
/* Ooops, need to materialize the inner plan */
inner_node = (Plan *) make_material(inner_tlist,
inner_node);
inner_plan = (Plan *) make_material(inner_tlist,
inner_plan);
break;
}
/*
* Now we can build the mergejoin node.
*/
join_node = make_mergejoin(tlist,
join_plan = make_mergejoin(tlist,
joinclauses,
otherclauses,
mergeclauses,
outer_node,
inner_node,
outer_plan,
inner_plan,
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 *
create_hashjoin_node(HashPath *best_path,
create_hashjoin_plan(HashPath *best_path,
List *tlist,
List *joinclauses,
List *otherclauses,
Plan *outer_node,
Plan *outer_plan,
List *outer_tlist,
Plan *inner_node,
Plan *inner_plan,
List *inner_tlist)
{
List *hashclauses;
HashJoin *join_node;
Hash *hash_node;
HashJoin *join_plan;
Hash *hash_plan;
Node *innerhashkey;
/*
@@ -811,18 +842,18 @@ create_hashjoin_node(HashPath *best_path,
/*
* Build the hash node and hash join node.
*/
hash_node = make_hash(inner_tlist, innerhashkey, inner_node);
join_node = make_hashjoin(tlist,
hash_plan = make_hash(inner_tlist, innerhashkey, inner_plan);
join_plan = make_hashjoin(tlist,
joinclauses,
otherclauses,
hashclauses,
outer_node,
(Plan *) hash_node,
outer_plan,
(Plan *) hash_plan,
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)
* but it helps produce more reasonable-looking EXPLAIN output.
* (Some callers alter the info after copying it.)
*/
void
static void
copy_plan_costsize(Plan *dest, Plan *src)
{
if (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,
SubqueryScan *node = makeNode(SubqueryScan);
Plan *plan = &node->scan.plan;
/* cost should be inserted by caller */
copy_plan_costsize(plan, subplan);
plan->state = (EState *) NULL;
plan->targetlist = qptlist;
plan->qual = qpqual;
@@ -1225,6 +1260,39 @@ make_subqueryscan(List *qptlist,
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 *
make_nestloop(List *tlist,

View File

@@ -14,7 +14,7 @@
*
*
* 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,
* tuple_fraction >= 1: tuple_fraction is the absolute number of tuples
* expected to be retrieved (ie, a LIMIT specification)
* 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.
*--------------------
@@ -125,9 +126,16 @@ query_planner(Query *root,
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,
/*
* 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
* node anyway, so no need for extra code here. (Furthermore, the
* let the caller do that. grouping_planner has to be able to add a
* sort node anyway, so no need for extra code here. (Furthermore, the
* given pathkeys might involve something we can't compute here, such
* as an aggregate function...)
*/

View File

@@ -8,7 +8,7 @@
*
*
* 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);
static Node *preprocess_jointree(Query *parse, Node *jtnode);
static Node *preprocess_expression(Query *parse, Node *expr, int kind);
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,
AttrNumber **groupColIdx);
static Plan *make_groupplan(List *group_tlist, bool tuplePerGroup,
@@ -65,7 +67,7 @@ planner(Query *parse)
/*
* 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.
*
* These vars cannot be moved into the Query structure since their
@@ -109,11 +111,14 @@ planner(Query *parse)
*
* parse is the querytree produced by the parser & rewriter.
* 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
* per Query object. It then calls union_planner, which may be called
* recursively on the same Query node in order to handle inheritance.
* per Query object. It then calls grouping_planner. At one time,
* 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
* found within the query's expressions and rangetable.
*
@@ -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 *)
preprocess_expression(parse, (Node *) parse->targetList,
@@ -176,17 +181,14 @@ subquery_planner(Query *parse, double tuple_fraction)
EXPRKIND_HAVING);
/*
* Do the main planning (potentially recursive for inheritance)
*/
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.
* Do the main planning. If we have an inherited target relation,
* that needs special processing, else go straight to grouping_planner.
*/
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,
@@ -600,10 +602,65 @@ preprocess_qual_conditions(Query *parse, Node *jtnode)
}
/*--------------------
* union_planner
* Invokes the planner on union-type queries (both set operations and
* appends produced by inheritance), recursing if necessary to get them
* all, then processes normal plans.
* inheritance_planner
* Generate a plan in the case where the result relation is an
* inheritance set.
*
* 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.
* tuple_fraction is the fraction of tuples we expect will be retrieved
@@ -621,18 +678,15 @@ preprocess_qual_conditions(Query *parse, Node *jtnode)
* Returns a query plan.
*--------------------
*/
Plan *
union_planner(Query *parse,
double tuple_fraction)
static Plan *
grouping_planner(Query *parse, double tuple_fraction)
{
List *tlist = parse->targetList;
Plan *result_plan = (Plan *) NULL;
AttrNumber *groupColIdx = NULL;
List *current_pathkeys = NIL;
Plan *result_plan;
List *current_pathkeys;
List *group_pathkeys;
List *sort_pathkeys;
Index rt_index;
List *inheritors;
AttrNumber *groupColIdx = NULL;
if (parse->setOperations)
{
@@ -654,12 +708,13 @@ union_planner(Query *parse,
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,
* since the appended-together results are unsorted even if the
* 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
@@ -670,54 +725,6 @@ union_planner(Query *parse,
sort_pathkeys = make_pathkeys_for_sortclauses(parse->sortClause,
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
{
List *sub_tlist;
@@ -938,10 +945,6 @@ union_planner(Query *parse,
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
* running query_planner(), so do it now.
@@ -1057,7 +1060,7 @@ union_planner(Query *parse,
* make_subplanTargetList
* 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
* target list to query_planner than the outer plan nodes should have.
* This routine generates the correct target list for the subplan.

View File

@@ -1,15 +1,20 @@
/*-------------------------------------------------------------------------
*
* prepunion.c
* Routines to plan set-operation and inheritance queries. The filename
* is a leftover from a time when only UNIONs were handled.
* Routines to plan set-operation queries. The filename is a leftover
* 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) 1994, Regents of the University of California
*
*
* 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 @@
#include "parser/parsetree.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
{
Index rt_index;
int sublevels_up;
Index old_rt_index;
Index new_rt_index;
Oid old_relid;
Oid new_relid;
} fix_parsetree_attnums_context;
} adjust_inherited_attrs_context;
static Plan *recurse_set_operations(Node *setOp, Query *parse,
List *colTypes, bool junkOK,
@@ -53,14 +64,8 @@ static List *generate_setop_tlist(List *colTypes, int flag,
List *input_tlist,
List *refnames_tlist);
static bool tlist_same_datatypes(List *tlist, List *colTypes, bool junkOK);
static void fix_parsetree_attnums(Index rt_index, Oid old_relid,
Oid new_relid, Query *parsetree);
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);
static Node *adjust_inherited_attrs_mutator(Node *node,
adjust_inherited_attrs_context *context);
/*
@@ -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)
*
* 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
* back in union_planner.
* Any top-level ORDER BY requested in parse->sortClause will be added
* when we return to grouping_planner.
*/
Plan *
plan_set_operations(Query *parse)
@@ -142,7 +147,6 @@ recurse_set_operations(Node *setOp, Query *parse,
NIL,
rtr->rtindex,
subplan);
copy_plan_costsize(plan, subplan);
return plan;
}
else if (IsA(setOp, SetOperationStmt))
@@ -217,8 +221,7 @@ generate_union_plan(SetOperationStmt *op, Query *parse,
*/
plan = (Plan *)
make_append(planlist,
0,
NIL,
false,
generate_setop_tlist(op->colTypes, -1, false,
((Plan *) lfirst(planlist))->targetlist,
refnames_tlist));
@@ -269,8 +272,7 @@ generate_nonunion_plan(SetOperationStmt *op, Query *parse,
*/
plan = (Plan *)
make_append(makeList2(lplan, rplan),
0,
NIL,
false,
generate_setop_tlist(op->colTypes, 0, false,
lplan->targetlist,
refnames_tlist));
@@ -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 -
* Returns an integer list of relids including the given rel plus
@@ -622,200 +498,181 @@ find_all_inheritors(Oid parentrel)
}
/*
* find_inheritable_rt_entry -
* Given a rangetable, find the first rangetable entry that represents
* an inheritance set.
* expand_inherted_rtentry
* Check whether a rangetable entry represents 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
* whole inheritance set (parent and children).
* If not, return NIL.
*
* If successful, set *rt_index to the index (1..n) of the entry,
* set *inheritors to a list of the relation OIDs of the set,
* and return TRUE.
* A childless table is never considered to be an inheritance set; therefore
* the result will never be a one-element list. It'll be either empty
* or have two or more elements.
*
* If there is no entry that requires inheritance processing,
* return FALSE.
*
* NOTE: We return the inheritors list so that plan_inherit_queries doesn't
* have to compute it again.
*
* NOTE: We clear the inh flag in any entries that have it set but turn
* out not to have any actual inheritance children. This is an efficiency
* hack to avoid having to repeat the inheritance checks if the list is
* scanned again (as will happen during expansion of any subsequent entry
* 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.
* NOTE: after this routine executes, the specified RTE will always have
* its inh flag cleared, whether or not there were any children. This
* ensures we won't expand the same RTE twice, which would otherwise occur
* for the case of an inherited UPDATE/DELETE target relation.
*/
bool
find_inheritable_rt_entry(List *rangetable,
Index *rt_index,
List **inheritors)
List *
expand_inherted_rtentry(Query *parse, Index rti)
{
Index count = 0;
List *temp;
RangeTblEntry *rte = rt_fetch(rti, parse->rtable);
Oid parentOID = rte->relid;
List *inhOIDs;
List *inhRTIs;
List *l;
foreach(temp, rangetable)
/* 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);
List *inhs;
Oid childOID = (Oid) lfirsti(l);
RangeTblEntry *childrte;
Index childRTindex;
count++;
/* Ignore non-inheritable RT entries */
if (! rt_entry->inh)
/* parent will be in the list too, so ignore it */
if (childOID == parentOID)
continue;
/* Fast path for common case of childless table */
if (! has_subclass(rt_entry->relid))
{
rt_entry->inh = false;
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
* no-child case. This could happen despite above has_subclass()
* check, if table once had a child but no longer does.
* Build an RTE for the child, and attach to query's rangetable list.
* We copy most fields of the parent's RTE, but replace relation
* real name and OID. Note that inh will be false at this point.
*/
if (lnext(inhs) == NIL)
{
rt_entry->inh = false;
continue;
}
/* OK, found our boy */
*rt_index = count;
*inheritors = inhs;
return true;
childrte = copyObject(rte);
childrte->relname = get_rel_name(childOID);
childrte->relid = childOID;
parse->rtable = lappend(parse->rtable, childrte);
childRTindex = length(parse->rtable);
inhRTIs = lappendi(inhRTIs, childRTindex);
}
return inhRTIs;
}
/*
* adjust_inherited_attrs
* Copy the specified query or expression and translate Vars referring
* to old_rt_index to refer to new_rt_index.
*
* We also adjust varattno to match the new table by column name, rather
* 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...
*/
Node *
adjust_inherited_attrs(Node *node,
Index old_rt_index, Oid old_relid,
Index new_rt_index, Oid new_relid)
{
adjust_inherited_attrs_context context;
/* Handle simple case simply... */
if (old_rt_index == new_rt_index)
{
Assert(old_relid == new_relid);
return copyObject(node);
}
return false;
}
/*
* 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
* Replaces attribute numbers from the relation represented by
* 'old_relid' in 'parsetree' with the attribute numbers from
* 'new_relid'.
*
* The parsetree is MODIFIED IN PLACE. This is OK only because
* plan_inherit_queries made a copy of the tree for us to hack upon.
*/
static void
fix_parsetree_attnums(Index rt_index,
Oid old_relid,
Oid new_relid,
Query *parsetree)
{
fix_parsetree_attnums_context context;
if (old_relid == new_relid)
return; /* no work needed for parent rel itself */
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.new_relid = new_relid;
context.sublevels_up = 0;
query_tree_walker(parsetree,
fix_parsetree_attnums_walker,
(void *) &context,
true);
/*
* Must be prepared to start with a Query or a bare expression tree.
*/
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);
}
/*
* Adjust varnos for child tables. This routine 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 bool
fix_parsetree_attnums_walker(Node *node,
fix_parsetree_attnums_context *context)
static Node *
adjust_inherited_attrs_mutator(Node *node,
adjust_inherited_attrs_context *context)
{
if (node == NULL)
return false;
return NULL;
if (IsA(node, Var))
{
Var *var = (Var *) node;
Var *var = (Var *) copyObject(node);
if (var->varlevelsup == context->sublevels_up &&
var->varno == context->rt_index &&
var->varattno > 0)
if (var->varlevelsup == 0 &&
var->varno == context->old_rt_index)
{
var->varattno = get_attnum(context->new_relid,
get_attname(context->old_relid,
var->varattno));
var->varno = context->new_rt_index;
if (var->varattno > 0)
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 */
bool result;
RangeTblRef *rtr = (RangeTblRef *) copyObject(node);
context->sublevels_up++;
result = query_tree_walker((Query *) node,
fix_parsetree_attnums_walker,
(void *) context,
true);
context->sublevels_up--;
return result;
if (rtr->rtindex == context->old_rt_index)
rtr->rtindex = context->new_rt_index;
return (Node *) rtr;
}
return expression_tree_walker(node, fix_parsetree_attnums_walker,
/*
* We have to process RestrictInfo nodes specially: we do NOT want to
* copy the original subclauseindices list, since the new rel may have
* different indices. The list will be rebuilt during planning anyway.
*/
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);
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);
}
static Append *
make_append(List *appendplans,
Index rt_index,
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;
node->plan.qual = NIL;
node->plan.lefttree = (Plan *) NULL;
node->plan.righttree = (Plan *) NULL;
return node;
}

View File

@@ -8,7 +8,7 @@
*
*
* 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)
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
* Creates a path corresponding to a sequential scan of a subquery,

View File

@@ -8,7 +8,7 @@
*
*
* 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)
{
List *baserels;
RelOptInfo *rel;
Oid relationObjectId;
foreach(baserels, root->base_rel_list)
{
@@ -60,7 +59,30 @@ get_base_rel(Query *root, int relid)
}
/* 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->rows = 0;
rel->width = 0;
@@ -95,8 +117,6 @@ get_base_rel(Query *root, int relid)
rel->issubquery = true;
}
root->base_rel_list = lcons(rel, root->base_rel_list);
return rel;
}

View File

@@ -8,7 +8,7 @@
*
*
* 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)
/*
* Since the rel was in the rangetable already, it's being read
* 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. */

View File

@@ -8,7 +8,7 @@
*
*
* 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)
state->es_direction = ForwardScanDirection;
state->es_range_table = NIL;
state->es_result_relations = NULL;
state->es_num_result_relations = 0;
state->es_result_relation_info = NULL;
state->es_junkFilter = NULL;
state->es_into_relation_descriptor = NULL;
state->es_param_list_info = NULL;
@@ -76,8 +81,6 @@ CreateExecutorState(void)
state->es_tupleTable = NULL;
state->es_junkFilter = NULL;
state->es_query_cxt = CurrentMemoryContext;
state->es_per_tuple_exprcontext = NULL;