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:
parent
609f9199af
commit
6543d81d65
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -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))
|
||||
|
@ -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,28 +365,12 @@ ExecCheckPlanPerms(Plan *plan, List *rangeTable, CmdType operation)
|
||||
Append *app = (Append *) plan;
|
||||
List *appendplans;
|
||||
|
||||
if (app->inheritrelid > 0)
|
||||
{
|
||||
/* 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);
|
||||
}
|
||||
}
|
||||
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 (resultRelations != NIL)
|
||||
{
|
||||
/*
|
||||
* if we have a result relation, open it and initialize the result
|
||||
* relation info stuff.
|
||||
* Multiple result relations (due to inheritance)
|
||||
* parseTree->resultRelations identifies them all
|
||||
*/
|
||||
RelationInfo *resultRelationInfo;
|
||||
Index resultRelationIndex;
|
||||
Oid resultRelationOid;
|
||||
Relation resultRelationDesc;
|
||||
ResultRelInfo *resultRelInfo;
|
||||
|
||||
resultRelationIndex = resultRelation;
|
||||
resultRelationOid = getrelid(resultRelationIndex, rangeTable);
|
||||
resultRelationDesc = heap_open(resultRelationOid, RowExclusiveLock);
|
||||
|
||||
if (resultRelationDesc->rd_rel->relkind == RELKIND_SEQUENCE)
|
||||
elog(ERROR, "You can't change sequence relation %s",
|
||||
RelationGetRelationName(resultRelationDesc));
|
||||
|
||||
if (resultRelationDesc->rd_rel->relkind == RELKIND_TOASTVALUE)
|
||||
elog(ERROR, "You can't change toast relation %s",
|
||||
RelationGetRelationName(resultRelationDesc));
|
||||
|
||||
if (resultRelationDesc->rd_rel->relkind == RELKIND_VIEW)
|
||||
elog(ERROR, "You can't change view relation %s",
|
||||
RelationGetRelationName(resultRelationDesc));
|
||||
|
||||
resultRelationInfo = makeNode(RelationInfo);
|
||||
resultRelationInfo->ri_RangeTableIndex = resultRelationIndex;
|
||||
resultRelationInfo->ri_RelationDesc = resultRelationDesc;
|
||||
resultRelationInfo->ri_NumIndices = 0;
|
||||
resultRelationInfo->ri_IndexRelationDescs = NULL;
|
||||
resultRelationInfo->ri_IndexRelationInfo = NULL;
|
||||
|
||||
/*
|
||||
* 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;
|
||||
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);
|
||||
}
|
||||
|
||||
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,13 +662,56 @@ 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;
|
||||
|
||||
/* Top plan had better be an Append here. */
|
||||
Assert(IsA(plan, Append));
|
||||
Assert(((Append *) plan)->isTarget);
|
||||
subplans = ((Append *) plan)->appendplans;
|
||||
Assert(length(subplans) == estate->es_num_result_relations);
|
||||
resultRelInfo = estate->es_result_relations;
|
||||
while (subplans != NIL)
|
||||
{
|
||||
Plan *subplan = (Plan *) lfirst(subplans);
|
||||
JunkFilter *j;
|
||||
|
||||
j = ExecInitJunkFilter(subplan->targetlist,
|
||||
ExecGetTupType(subplan));
|
||||
resultRelInfo->ri_junkFilter = j;
|
||||
resultRelInfo++;
|
||||
subplans = lnext(subplans);
|
||||
}
|
||||
/*
|
||||
* Set active junkfilter too; at this point ExecInitAppend
|
||||
* has already selected an active result relation...
|
||||
*/
|
||||
estate->es_junkFilter =
|
||||
estate->es_result_relation_info->ri_junkFilter;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Normal case with just one JunkFilter */
|
||||
JunkFilter *j = ExecInitJunkFilter(plan->targetlist,
|
||||
tupType);
|
||||
|
||||
estate->es_junkFilter = j;
|
||||
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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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:
|
||||
{
|
||||
|
@ -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);
|
||||
|
||||
/*
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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 */
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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,10 +118,30 @@ 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 */
|
||||
set_plain_rel_pathlist(root, rel, rte);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 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 */
|
||||
@ -143,13 +172,109 @@ set_base_rel_pathlist(Query *root)
|
||||
* 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.
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
plan = (Join *) create_mergejoin_plan((MergePath *) best_path,
|
||||
join_tlist,
|
||||
joinclauses,
|
||||
otherclauses,
|
||||
outer_node,
|
||||
outer_plan,
|
||||
outer_tlist,
|
||||
inner_node,
|
||||
inner_plan,
|
||||
inner_tlist);
|
||||
break;
|
||||
case T_HashJoin:
|
||||
retval = (Join *) create_hashjoin_node((HashPath *) best_path,
|
||||
tlist,
|
||||
plan = (Join *) create_hashjoin_plan((HashPath *) best_path,
|
||||
join_tlist,
|
||||
joinclauses,
|
||||
otherclauses,
|
||||
outer_node,
|
||||
outer_plan,
|
||||
outer_tlist,
|
||||
inner_node,
|
||||
inner_plan,
|
||||
inner_tlist);
|
||||
break;
|
||||
case T_NestLoop:
|
||||
retval = (Join *) create_nestloop_node((NestPath *) best_path,
|
||||
tlist,
|
||||
plan = (Join *) create_nestloop_plan((NestPath *) best_path,
|
||||
join_tlist,
|
||||
joinclauses,
|
||||
otherclauses,
|
||||
outer_node,
|
||||
outer_plan,
|
||||
outer_tlist,
|
||||
inner_node,
|
||||
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,
|
||||
|
@ -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...)
|
||||
*/
|
||||
|
@ -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.
|
||||
|
@ -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)
|
||||
{
|
||||
RangeTblEntry *rt_entry = (RangeTblEntry *) lfirst(temp);
|
||||
List *inhs;
|
||||
|
||||
count++;
|
||||
/* Ignore non-inheritable RT entries */
|
||||
if (! rt_entry->inh)
|
||||
continue;
|
||||
/* 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(rt_entry->relid))
|
||||
{
|
||||
rt_entry->inh = false;
|
||||
continue;
|
||||
}
|
||||
if (! has_subclass(parentOID))
|
||||
return NIL;
|
||||
/* Scan for all members of inheritance set */
|
||||
inhs = find_all_inheritors(rt_entry->relid);
|
||||
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(inhs) == NIL)
|
||||
if (lnext(inhOIDs) == NIL)
|
||||
return NIL;
|
||||
/* OK, it's an inheritance set; expand it */
|
||||
inhRTIs = makeListi1(rti);
|
||||
foreach(l, inhOIDs)
|
||||
{
|
||||
rt_entry->inh = false;
|
||||
Oid childOID = (Oid) lfirsti(l);
|
||||
RangeTblEntry *childrte;
|
||||
Index childRTindex;
|
||||
|
||||
/* parent will be in the list too, so ignore it */
|
||||
if (childOID == parentOID)
|
||||
continue;
|
||||
}
|
||||
/* OK, found our boy */
|
||||
*rt_index = count;
|
||||
*inheritors = inhs;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
/*
|
||||
* new_rangetable_entry -
|
||||
* Replaces the name and relid of 'old_entry' with the values for
|
||||
* 'new_relid'.
|
||||
* adjust_inherited_attrs
|
||||
* Copy the specified query or expression and translate Vars referring
|
||||
* to old_rt_index to refer to new_rt_index.
|
||||
*
|
||||
* Returns a copy of 'old_entry' with the parameters substituted.
|
||||
* 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...
|
||||
*/
|
||||
static RangeTblEntry *
|
||||
new_rangetable_entry(Oid new_relid, RangeTblEntry *old_entry)
|
||||
Node *
|
||||
adjust_inherited_attrs(Node *node,
|
||||
Index old_rt_index, Oid old_relid,
|
||||
Index new_rt_index, Oid new_relid)
|
||||
{
|
||||
RangeTblEntry *new_entry = copyObject(old_entry);
|
||||
adjust_inherited_attrs_context context;
|
||||
|
||||
/* 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;
|
||||
}
|
||||
/* Handle simple case simply... */
|
||||
if (old_rt_index == new_rt_index)
|
||||
{
|
||||
Assert(old_relid == new_relid);
|
||||
return copyObject(node);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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->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;
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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. */
|
||||
|
@ -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;
|
||||
|
@ -37,7 +37,7 @@
|
||||
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: catversion.h,v 1.58 2000/11/11 19:55:33 thomas Exp $
|
||||
* $Id: catversion.h,v 1.59 2000/11/12 00:37:00 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -53,6 +53,6 @@
|
||||
*/
|
||||
|
||||
/* yyyymmddN */
|
||||
#define CATALOG_VERSION_NO 200011110
|
||||
#define CATALOG_VERSION_NO 200011112
|
||||
|
||||
#endif
|
||||
|
@ -7,7 +7,7 @@
|
||||
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: executor.h,v 1.52 2000/10/26 21:38:03 tgl Exp $
|
||||
* $Id: executor.h,v 1.53 2000/11/12 00:37:01 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -56,7 +56,7 @@ extern TupleDesc ExecutorStart(QueryDesc *queryDesc, EState *estate);
|
||||
extern TupleTableSlot *ExecutorRun(QueryDesc *queryDesc, EState *estate,
|
||||
int feature, long count);
|
||||
extern void ExecutorEnd(QueryDesc *queryDesc, EState *estate);
|
||||
extern void ExecConstraints(char *caller, Relation rel,
|
||||
extern void ExecConstraints(char *caller, ResultRelInfo *resultRelInfo,
|
||||
TupleTableSlot *slot, EState *estate);
|
||||
extern TupleTableSlot *EvalPlanQual(EState *estate, Index rti,
|
||||
ItemPointer tid);
|
||||
@ -153,8 +153,8 @@ extern void FreeExprContext(ExprContext *econtext);
|
||||
#define ResetExprContext(econtext) \
|
||||
MemoryContextReset((econtext)->ecxt_per_tuple_memory)
|
||||
|
||||
extern void ExecOpenIndices(RelationInfo *resultRelationInfo);
|
||||
extern void ExecCloseIndices(RelationInfo *resultRelationInfo);
|
||||
extern void ExecOpenIndices(ResultRelInfo *resultRelInfo);
|
||||
extern void ExecCloseIndices(ResultRelInfo *resultRelInfo);
|
||||
extern void ExecInsertIndexTuples(TupleTableSlot *slot, ItemPointer tupleid,
|
||||
EState *estate, bool is_update);
|
||||
|
||||
|
@ -7,7 +7,7 @@
|
||||
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: tuptable.h,v 1.15 2000/01/26 05:58:06 momjian Exp $
|
||||
* $Id: tuptable.h,v 1.16 2000/11/12 00:37:01 tgl Exp $
|
||||
*
|
||||
* NOTES
|
||||
* The tuple table interface is getting pretty ugly.
|
||||
@ -56,7 +56,6 @@ typedef struct TupleTableSlot
|
||||
bool ttc_descIsNew;
|
||||
TupleDesc ttc_tupleDescriptor;
|
||||
Buffer ttc_buffer;
|
||||
int ttc_whichplan;
|
||||
} TupleTableSlot;
|
||||
|
||||
/* ----------------
|
||||
|
@ -7,7 +7,7 @@
|
||||
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: execnodes.h,v 1.52 2000/10/26 21:38:12 tgl Exp $
|
||||
* $Id: execnodes.h,v 1.53 2000/11/12 00:37:01 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -52,31 +52,6 @@ typedef struct IndexInfo
|
||||
bool ii_Unique;
|
||||
} IndexInfo;
|
||||
|
||||
/* ----------------
|
||||
* RelationInfo information
|
||||
*
|
||||
* whenever we update an existing relation, we have to
|
||||
* update indices on the relation. The RelationInfo class
|
||||
* is used to hold all the information on result relations,
|
||||
* including indices.. -cim 10/15/89
|
||||
*
|
||||
* RangeTableIndex result relation's range table index
|
||||
* RelationDesc relation descriptor for result relation
|
||||
* NumIndices number indices existing on result relation
|
||||
* IndexRelationDescs array of relation descriptors for indices
|
||||
* IndexRelationInfo array of key/attr info for indices
|
||||
* ----------------
|
||||
*/
|
||||
typedef struct RelationInfo
|
||||
{
|
||||
NodeTag type;
|
||||
Index ri_RangeTableIndex;
|
||||
Relation ri_RelationDesc;
|
||||
int ri_NumIndices;
|
||||
RelationPtr ri_IndexRelationDescs;
|
||||
IndexInfo **ri_IndexRelationInfo;
|
||||
} RelationInfo;
|
||||
|
||||
/* ----------------
|
||||
* ExprContext
|
||||
*
|
||||
@ -116,8 +91,6 @@ typedef struct ExprContext
|
||||
/* Values to substitute for Aggref nodes in expression */
|
||||
Datum *ecxt_aggvalues; /* precomputed values for Aggref nodes */
|
||||
bool *ecxt_aggnulls; /* null flags for Aggref nodes */
|
||||
/* Range table that Vars in expression refer to --- seldom needed */
|
||||
List *ecxt_range_table;
|
||||
} ExprContext;
|
||||
|
||||
/*
|
||||
@ -210,6 +183,35 @@ typedef struct JunkFilter
|
||||
AttrNumber *jf_cleanMap;
|
||||
} JunkFilter;
|
||||
|
||||
/* ----------------
|
||||
* ResultRelInfo information
|
||||
*
|
||||
* whenever we update an existing relation, we have to
|
||||
* update indices on the relation. The ResultRelInfo class
|
||||
* is used to hold all the information on result relations,
|
||||
* including indices.. -cim 10/15/89
|
||||
*
|
||||
* RangeTableIndex result relation's range table index
|
||||
* RelationDesc relation descriptor for result relation
|
||||
* NumIndices # of indices existing on result relation
|
||||
* IndexRelationDescs array of relation descriptors for indices
|
||||
* IndexRelationInfo array of key/attr info for indices
|
||||
* ConstraintExprs array of constraint-checking expressions
|
||||
* junkFilter for removing junk attributes from tuples
|
||||
* ----------------
|
||||
*/
|
||||
typedef struct ResultRelInfo
|
||||
{
|
||||
NodeTag type;
|
||||
Index ri_RangeTableIndex;
|
||||
Relation ri_RelationDesc;
|
||||
int ri_NumIndices;
|
||||
RelationPtr ri_IndexRelationDescs;
|
||||
IndexInfo **ri_IndexRelationInfo;
|
||||
List **ri_ConstraintExprs;
|
||||
JunkFilter *ri_junkFilter;
|
||||
} ResultRelInfo;
|
||||
|
||||
/* ----------------
|
||||
* EState information
|
||||
*
|
||||
@ -217,7 +219,7 @@ typedef struct JunkFilter
|
||||
*
|
||||
* range_table array of scan relation information
|
||||
*
|
||||
* result_relation_information for update queries
|
||||
* result_relation information for insert/update/delete queries
|
||||
*
|
||||
* into_relation_descriptor relation being retrieved "into"
|
||||
*
|
||||
@ -227,10 +229,6 @@ typedef struct JunkFilter
|
||||
* tupleTable this is a pointer to an array
|
||||
* of pointers to tuples used by
|
||||
* the executor at any given moment.
|
||||
*
|
||||
* junkFilter contains information used to
|
||||
* extract junk attributes from a tuple.
|
||||
* (see JunkFilter above)
|
||||
* ----------------
|
||||
*/
|
||||
typedef struct EState
|
||||
@ -239,23 +237,24 @@ typedef struct EState
|
||||
ScanDirection es_direction;
|
||||
Snapshot es_snapshot;
|
||||
List *es_range_table;
|
||||
RelationInfo *es_result_relation_info;
|
||||
ResultRelInfo *es_result_relations; /* array of ResultRelInfos */
|
||||
int es_num_result_relations; /* length of array */
|
||||
ResultRelInfo *es_result_relation_info; /* currently active array elt */
|
||||
JunkFilter *es_junkFilter; /* currently active junk filter */
|
||||
Relation es_into_relation_descriptor;
|
||||
ParamListInfo es_param_list_info;
|
||||
ParamExecData *es_param_exec_vals; /* this is for subselects */
|
||||
TupleTable es_tupleTable;
|
||||
JunkFilter *es_junkFilter;
|
||||
uint32 es_processed; /* # of tuples processed */
|
||||
Oid es_lastoid; /* last oid processed (by INSERT) */
|
||||
List *es_rowMark; /* not good place, but there is no other */
|
||||
MemoryContext es_query_cxt; /* per-query context in which EState lives */
|
||||
/* this ExprContext is for per-output-tuple operations, such as
|
||||
/*
|
||||
* this ExprContext is for per-output-tuple operations, such as
|
||||
* constraint checks and index-value computations. It can be reset
|
||||
* for each output tuple. Note that it will be created only if needed.
|
||||
*/
|
||||
ExprContext *es_per_tuple_exprcontext;
|
||||
/* this field is storage space for ExecConstraints(): */
|
||||
List **es_result_relation_constraints;
|
||||
/* Below is to re-evaluate plan qual in READ COMMITTED mode */
|
||||
struct Plan *es_origPlan;
|
||||
Pointer es_evalPlanQual;
|
||||
@ -341,15 +340,9 @@ typedef struct ResultState
|
||||
/* ----------------
|
||||
* AppendState information
|
||||
*
|
||||
* append nodes have this field "unionplans" which is this
|
||||
* list of plans to execute in sequence.. these variables
|
||||
* keep track of things..
|
||||
*
|
||||
* whichplan which plan is being executed
|
||||
* whichplan which plan is being executed (0 .. n-1)
|
||||
* nplans how many plans are in the list
|
||||
* initialized array of ExecInitNode() results
|
||||
* result_relation_info_list array of each subplan's result relation info
|
||||
* junkFilter_list array of each subplan's junk filter
|
||||
* ----------------
|
||||
*/
|
||||
typedef struct AppendState
|
||||
@ -358,8 +351,6 @@ typedef struct AppendState
|
||||
int as_whichplan;
|
||||
int as_nplans;
|
||||
bool *as_initialized;
|
||||
List *as_result_relation_info_list;
|
||||
List *as_junkFilter_list;
|
||||
} AppendState;
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
|
@ -7,7 +7,7 @@
|
||||
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: nodes.h,v 1.82 2000/11/05 22:50:21 vadim Exp $
|
||||
* $Id: nodes.h,v 1.83 2000/11/12 00:37:01 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -83,11 +83,12 @@ typedef enum NodeTag
|
||||
T_NestPath,
|
||||
T_MergePath,
|
||||
T_HashPath,
|
||||
T_TidPath,
|
||||
T_AppendPath,
|
||||
T_PathKeyItem,
|
||||
T_RestrictInfo,
|
||||
T_JoinInfo,
|
||||
T_Stream,
|
||||
T_TidPath,
|
||||
T_IndexOptInfo,
|
||||
|
||||
/*---------------------
|
||||
@ -95,7 +96,7 @@ typedef enum NodeTag
|
||||
*---------------------
|
||||
*/
|
||||
T_IndexInfo = 300,
|
||||
T_RelationInfo,
|
||||
T_ResultRelInfo,
|
||||
T_TupleCount,
|
||||
T_TupleTableSlot,
|
||||
T_ExprContext,
|
||||
|
@ -7,7 +7,7 @@
|
||||
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: parsenodes.h,v 1.119 2000/11/05 22:50:21 vadim Exp $
|
||||
* $Id: parsenodes.h,v 1.120 2000/11/12 00:37:01 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -70,6 +70,16 @@ typedef struct Query
|
||||
Node *setOperations; /* set-operation tree if this is top level
|
||||
* of a UNION/INTERSECT/EXCEPT query */
|
||||
|
||||
/*
|
||||
* If the resultRelation turns out to be the parent of an inheritance
|
||||
* tree, the planner will add all the child tables to the rtable and
|
||||
* store a list of the rtindexes of all the result relations here.
|
||||
* This is done at plan time, not parse time, since we don't want to
|
||||
* commit to the exact set of child tables at parse time. This field
|
||||
* ought to go in some sort of TopPlan plan node, not in the Query.
|
||||
*/
|
||||
List *resultRelations; /* integer list of RT indexes, or NIL */
|
||||
|
||||
/* internal to planner */
|
||||
List *base_rel_list; /* list of base-relation RelOptInfos */
|
||||
List *join_rel_list; /* list of join-relation RelOptInfos */
|
||||
|
@ -7,7 +7,7 @@
|
||||
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: plannodes.h,v 1.45 2000/10/26 21:38:12 tgl Exp $
|
||||
* $Id: plannodes.h,v 1.46 2000/11/12 00:37:01 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -134,8 +134,10 @@ typedef struct Plan
|
||||
|
||||
|
||||
/* ----------------
|
||||
* result node -
|
||||
* returns tuples from outer plan that satisfy the qualifications
|
||||
* Result node -
|
||||
* If no outer plan, evaluate a variable-free targetlist.
|
||||
* If outer plan, return tuples from outer plan that satisfy
|
||||
* given quals (we can also do a level of projection)
|
||||
* ----------------
|
||||
*/
|
||||
typedef struct Result
|
||||
@ -146,21 +148,21 @@ typedef struct Result
|
||||
} Result;
|
||||
|
||||
/* ----------------
|
||||
* append node
|
||||
* Append node -
|
||||
* Generate the concatenation of the results of sub-plans.
|
||||
*
|
||||
* Append nodes can modify the query's rtable during execution.
|
||||
* If inheritrelid > 0, then the RTE with index inheritrelid is replaced
|
||||
* by the i'th element of inheritrtable to execute the i'th subplan.
|
||||
* We assume that this RTE is not used in any other part of the
|
||||
* query plan tree, else confusion may result...
|
||||
* Append nodes are sometimes used to switch between several result relations
|
||||
* (when the target of an UPDATE or DELETE is an inheritance set). Such a
|
||||
* node will have isTarget true. The Append executor is then responsible
|
||||
* for updating the executor state to point at the correct target relation
|
||||
* whenever it switches subplans.
|
||||
* ----------------
|
||||
*/
|
||||
typedef struct Append
|
||||
{
|
||||
Plan plan;
|
||||
List *appendplans;
|
||||
Index inheritrelid;
|
||||
List *inheritrtable;
|
||||
bool isTarget;
|
||||
AppendState *appendstate;
|
||||
} Append;
|
||||
|
||||
|
@ -7,7 +7,7 @@
|
||||
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: relation.h,v 1.49 2000/09/29 18:21:39 tgl Exp $
|
||||
* $Id: relation.h,v 1.50 2000/11/12 00:37:01 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -82,6 +82,14 @@ typedef enum CostSelector
|
||||
* upon creation of the RelOptInfo object; they are filled in when
|
||||
* set_base_rel_pathlist processes the object.
|
||||
*
|
||||
* Note: if a base relation is the root of an inheritance tree
|
||||
* (SELECT FROM foo*) it is still considered a base rel. We will
|
||||
* generate a list of candidate Paths for accessing that table itself,
|
||||
* and also generate baserel RelOptInfo nodes for each child table,
|
||||
* with their own candidate Path lists. Then, an AppendPath is built
|
||||
* from the cheapest Path for each of these tables, and set to be the
|
||||
* only available Path for the inheritance baserel.
|
||||
*
|
||||
* * The presence of the remaining fields depends on the restrictions
|
||||
* and joins that the relation participates in:
|
||||
*
|
||||
@ -313,6 +321,9 @@ typedef struct IndexPath
|
||||
double rows; /* estimated number of result tuples */
|
||||
} IndexPath;
|
||||
|
||||
/*
|
||||
* TidPath represents a scan by TID
|
||||
*/
|
||||
typedef struct TidPath
|
||||
{
|
||||
Path path;
|
||||
@ -320,6 +331,17 @@ typedef struct TidPath
|
||||
Relids unjoined_relids;/* some rels not yet part of my Path */
|
||||
} TidPath;
|
||||
|
||||
/*
|
||||
* AppendPath represents an Append plan, ie, successive execution of
|
||||
* several member plans. Currently it is only used to handle expansion
|
||||
* of inheritance trees.
|
||||
*/
|
||||
typedef struct AppendPath
|
||||
{
|
||||
Path path;
|
||||
List *subpaths; /* list of component Paths */
|
||||
} AppendPath;
|
||||
|
||||
/*
|
||||
* All join-type paths share these fields.
|
||||
*/
|
||||
|
@ -7,7 +7,7 @@
|
||||
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: pathnode.h,v 1.30 2000/10/05 19:48:33 momjian Exp $
|
||||
* $Id: pathnode.h,v 1.31 2000/11/12 00:37:01 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -32,6 +32,7 @@ extern IndexPath *create_index_path(Query *root, RelOptInfo *rel,
|
||||
List *restriction_clauses,
|
||||
ScanDirection indexscandir);
|
||||
extern TidPath *create_tidscan_path(RelOptInfo *rel, List *tideval);
|
||||
extern AppendPath *create_append_path(RelOptInfo *rel, List *subpaths);
|
||||
extern Path *create_subqueryscan_path(RelOptInfo *rel);
|
||||
|
||||
extern NestPath *create_nestloop_path(RelOptInfo *joinrel,
|
||||
@ -63,6 +64,7 @@ extern HashPath *create_hashjoin_path(RelOptInfo *joinrel,
|
||||
* prototypes for relnode.c
|
||||
*/
|
||||
extern RelOptInfo *get_base_rel(Query *root, int relid);
|
||||
extern RelOptInfo *make_base_rel(Query *root, int relid);
|
||||
extern RelOptInfo *get_join_rel(Query *root, RelOptInfo *outer_rel,
|
||||
RelOptInfo *inner_rel,
|
||||
List **restrictlist_ptr);
|
||||
|
@ -7,7 +7,7 @@
|
||||
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: planmain.h,v 1.47 2000/10/26 21:38:24 tgl Exp $
|
||||
* $Id: planmain.h,v 1.48 2000/11/12 00:37:01 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -28,6 +28,7 @@ extern Plan *query_planner(Query *root, List *tlist, double tuple_fraction);
|
||||
extern Plan *create_plan(Query *root, Path *best_path);
|
||||
extern SubqueryScan *make_subqueryscan(List *qptlist, List *qpqual,
|
||||
Index scanrelid, Plan *subplan);
|
||||
extern Append *make_append(List *appendplans, bool isTarget, List *tlist);
|
||||
extern Sort *make_sort(List *tlist, Plan *lefttree, int keycount);
|
||||
extern Sort *make_sort_from_pathkeys(List *tlist, Plan *lefttree,
|
||||
List *pathkeys);
|
||||
@ -41,7 +42,6 @@ extern Limit *make_limit(List *tlist, Plan *lefttree,
|
||||
extern SetOp *make_setop(SetOpCmd cmd, List *tlist, Plan *lefttree,
|
||||
List *distinctList, AttrNumber flagColIdx);
|
||||
extern Result *make_result(List *tlist, Node *resconstantqual, Plan *subplan);
|
||||
extern void copy_plan_costsize(Plan *dest, Plan *src);
|
||||
|
||||
/*
|
||||
* prototypes for plan/initsplan.c
|
||||
|
@ -7,7 +7,7 @@
|
||||
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: planner.h,v 1.17 2000/10/05 19:11:37 tgl Exp $
|
||||
* $Id: planner.h,v 1.18 2000/11/12 00:37:01 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -20,7 +20,6 @@
|
||||
|
||||
extern Plan *planner(Query *parse);
|
||||
extern Plan *subquery_planner(Query *parse, double tuple_fraction);
|
||||
extern Plan *union_planner(Query *parse, double tuple_fraction);
|
||||
|
||||
extern Plan *make_sortplan(List *tlist, Plan *plannode, List *sortcls);
|
||||
|
||||
|
@ -7,7 +7,7 @@
|
||||
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: prep.h,v 1.24 2000/10/05 19:11:37 tgl Exp $
|
||||
* $Id: prep.h,v 1.25 2000/11/12 00:37:01 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -33,10 +33,12 @@ extern List *preprocess_targetlist(List *tlist, int command_type,
|
||||
* prototypes for prepunion.c
|
||||
*/
|
||||
extern Plan *plan_set_operations(Query *parse);
|
||||
|
||||
extern List *find_all_inheritors(Oid parentrel);
|
||||
extern bool find_inheritable_rt_entry(List *rangetable,
|
||||
Index *rt_index, List **inheritors);
|
||||
extern Plan *plan_inherit_queries(Query *root, List *tlist,
|
||||
Index rt_index, List *inheritors);
|
||||
extern List *expand_inherted_rtentry(Query *parse, Index rti);
|
||||
|
||||
extern Node *adjust_inherited_attrs(Node *node,
|
||||
Index old_rt_index, Oid old_relid,
|
||||
Index new_rt_index, Oid new_relid);
|
||||
|
||||
#endif /* PREP_H */
|
||||
|
Loading…
Reference in New Issue
Block a user