diff --git a/src/backend/commands/command.c b/src/backend/commands/command.c index 54b913dcac1..073360c1e4a 100644 --- a/src/backend/commands/command.c +++ b/src/backend/commands/command.c @@ -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); diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c index ea90e0f2e04..2877999500c 100644 --- a/src/backend/commands/copy.c +++ b/src/backend/commands/copy.c @@ -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); } diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c index 6976278c1d0..b76d45480f2 100644 --- a/src/backend/commands/explain.c +++ b/src/backend/commands/explain.c @@ -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)) diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index a26acc9a763..d9d02654970 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -27,7 +27,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.131 2000/10/26 21:35:15 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.132 2000/11/12 00:36:57 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -50,6 +50,10 @@ static TupleDesc InitPlan(CmdType operation, Query *parseTree, Plan *plan, EState *estate); +static void initResultRelInfo(ResultRelInfo *resultRelInfo, + Index resultRelationIndex, + List *rangeTable, + CmdType operation); static void EndPlan(Plan *plan, EState *estate); static TupleTableSlot *ExecutePlan(EState *estate, Plan *plan, CmdType operation, @@ -310,10 +314,6 @@ ExecCheckQueryPerms(CmdType operation, Query *parseTree, Plan *plan) * ExecCheckPlanPerms * Recursively scan the plan tree to check access permissions in * subplans. - * - * We also need to look at the local rangetables in Append plan nodes, - * which is pretty bogus --- most likely, those tables should be mentioned - * in the query's main rangetable. But at the moment, they're not. */ static void ExecCheckPlanPerms(Plan *plan, List *rangeTable, CmdType operation) @@ -365,27 +365,11 @@ ExecCheckPlanPerms(Plan *plan, List *rangeTable, CmdType operation) Append *app = (Append *) plan; List *appendplans; - if (app->inheritrelid > 0) + foreach(appendplans, app->appendplans) { - /* Append implements expansion of inheritance */ - ExecCheckRTPerms(app->inheritrtable, operation); - - foreach(appendplans, app->appendplans) - { - ExecCheckPlanPerms((Plan *) lfirst(appendplans), - rangeTable, - operation); - } - } - else - { - /* Append implements UNION, which must be a SELECT */ - foreach(appendplans, app->appendplans) - { - ExecCheckPlanPerms((Plan *) lfirst(appendplans), - rangeTable, - CMD_SELECT); - } + ExecCheckPlanPerms((Plan *) lfirst(appendplans), + rangeTable, + operation); } break; } @@ -518,10 +502,8 @@ static TupleDesc InitPlan(CmdType operation, Query *parseTree, Plan *plan, EState *estate) { List *rangeTable; - int resultRelation; Relation intoRelationDesc; TupleDesc tupType; - List *targetList; /* * Do permissions checks. @@ -532,7 +514,6 @@ InitPlan(CmdType operation, Query *parseTree, Plan *plan, EState *estate) * get information from query descriptor */ rangeTable = parseTree->rtable; - resultRelation = parseTree->resultRelation; /* * initialize the node's execution state @@ -540,63 +521,61 @@ InitPlan(CmdType operation, Query *parseTree, Plan *plan, EState *estate) estate->es_range_table = rangeTable; /* - * initialize result relation stuff + * if there is a result relation, initialize result relation stuff */ - - if (resultRelation != 0 && operation != CMD_SELECT) + if (parseTree->resultRelation != 0 && operation != CMD_SELECT) { + List *resultRelations = parseTree->resultRelations; + int numResultRelations; + ResultRelInfo *resultRelInfos; - /* - * if we have a result relation, open it and initialize the result - * relation info stuff. - */ - RelationInfo *resultRelationInfo; - Index resultRelationIndex; - Oid resultRelationOid; - Relation resultRelationDesc; + if (resultRelations != NIL) + { + /* + * Multiple result relations (due to inheritance) + * parseTree->resultRelations identifies them all + */ + ResultRelInfo *resultRelInfo; - resultRelationIndex = resultRelation; - resultRelationOid = getrelid(resultRelationIndex, rangeTable); - resultRelationDesc = heap_open(resultRelationOid, RowExclusiveLock); + numResultRelations = length(resultRelations); + resultRelInfos = (ResultRelInfo *) + palloc(numResultRelations * sizeof(ResultRelInfo)); + resultRelInfo = resultRelInfos; + while (resultRelations != NIL) + { + initResultRelInfo(resultRelInfo, + lfirsti(resultRelations), + rangeTable, + operation); + resultRelInfo++; + resultRelations = lnext(resultRelations); + } + } + else + { + /* + * Single result relation identified by parseTree->resultRelation + */ + numResultRelations = 1; + resultRelInfos = (ResultRelInfo *) palloc(sizeof(ResultRelInfo)); + initResultRelInfo(resultRelInfos, + parseTree->resultRelation, + rangeTable, + operation); + } - if (resultRelationDesc->rd_rel->relkind == RELKIND_SEQUENCE) - elog(ERROR, "You can't change sequence relation %s", - RelationGetRelationName(resultRelationDesc)); - - if (resultRelationDesc->rd_rel->relkind == RELKIND_TOASTVALUE) - elog(ERROR, "You can't change toast relation %s", - RelationGetRelationName(resultRelationDesc)); - - if (resultRelationDesc->rd_rel->relkind == RELKIND_VIEW) - elog(ERROR, "You can't change view relation %s", - RelationGetRelationName(resultRelationDesc)); - - resultRelationInfo = makeNode(RelationInfo); - resultRelationInfo->ri_RangeTableIndex = resultRelationIndex; - resultRelationInfo->ri_RelationDesc = resultRelationDesc; - resultRelationInfo->ri_NumIndices = 0; - resultRelationInfo->ri_IndexRelationDescs = NULL; - resultRelationInfo->ri_IndexRelationInfo = NULL; - - /* - * If there are indices on the result relation, open them and save - * descriptors in the result relation info, so that we can add new - * index entries for the tuples we add/update. We need not do - * this for a DELETE, however, since deletion doesn't affect - * indexes. - */ - if (resultRelationDesc->rd_rel->relhasindex && - operation != CMD_DELETE) - ExecOpenIndices(resultRelationInfo); - - estate->es_result_relation_info = resultRelationInfo; + estate->es_result_relations = resultRelInfos; + estate->es_num_result_relations = numResultRelations; + /* Initialize to first or only result rel */ + estate->es_result_relation_info = resultRelInfos; } else { - /* * if no result relation, then set state appropriately */ + estate->es_result_relations = NULL; + estate->es_num_result_relations = 0; estate->es_result_relation_info = NULL; } @@ -642,19 +621,17 @@ InitPlan(CmdType operation, Query *parseTree, Plan *plan, EState *estate) ExecInitNode(plan, estate, NULL); /* - * get the tuple descriptor describing the type of tuples to return.. + * Get the tuple descriptor describing the type of tuples to return. * (this is especially important if we are creating a relation with * "retrieve into") */ tupType = ExecGetTupType(plan); /* tuple descriptor */ - targetList = plan->targetlist; /* - * Now that we have the target list, initialize the junk filter if - * needed. SELECT and INSERT queries need a filter if there are any - * junk attrs in the tlist. UPDATE and DELETE always need one, since - * there's always a junk 'ctid' attribute present --- no need to look - * first. + * Initialize the junk filter if needed. SELECT and INSERT queries need + * a filter if there are any junk attrs in the tlist. UPDATE and + * DELETE always need one, since there's always a junk 'ctid' attribute + * present --- no need to look first. */ { bool junk_filter_needed = false; @@ -664,7 +641,7 @@ InitPlan(CmdType operation, Query *parseTree, Plan *plan, EState *estate) { case CMD_SELECT: case CMD_INSERT: - foreach(tlist, targetList) + foreach(tlist, plan->targetlist) { TargetEntry *tle = (TargetEntry *) lfirst(tlist); @@ -685,12 +662,55 @@ InitPlan(CmdType operation, Query *parseTree, Plan *plan, EState *estate) if (junk_filter_needed) { - JunkFilter *j = ExecInitJunkFilter(targetList, tupType); + /* + * If there are multiple result relations, each one needs + * its own junk filter. Note this is only possible for + * UPDATE/DELETE, so we can't be fooled by some needing + * a filter and some not. + */ + if (parseTree->resultRelations != NIL) + { + List *subplans; + ResultRelInfo *resultRelInfo; - estate->es_junkFilter = j; + /* Top plan had better be an Append here. */ + Assert(IsA(plan, Append)); + Assert(((Append *) plan)->isTarget); + subplans = ((Append *) plan)->appendplans; + Assert(length(subplans) == estate->es_num_result_relations); + resultRelInfo = estate->es_result_relations; + while (subplans != NIL) + { + Plan *subplan = (Plan *) lfirst(subplans); + JunkFilter *j; - if (operation == CMD_SELECT) - tupType = j->jf_cleanTupType; + j = ExecInitJunkFilter(subplan->targetlist, + ExecGetTupType(subplan)); + resultRelInfo->ri_junkFilter = j; + resultRelInfo++; + subplans = lnext(subplans); + } + /* + * Set active junkfilter too; at this point ExecInitAppend + * has already selected an active result relation... + */ + estate->es_junkFilter = + estate->es_result_relation_info->ri_junkFilter; + } + else + { + /* Normal case with just one JunkFilter */ + JunkFilter *j = ExecInitJunkFilter(plan->targetlist, + tupType); + + estate->es_junkFilter = j; + if (estate->es_result_relation_info) + estate->es_result_relation_info->ri_junkFilter = j; + + /* For SELECT, want to return the cleaned tuple type */ + if (operation == CMD_SELECT) + tupType = j->jf_cleanTupType; + } } else estate->es_junkFilter = NULL; @@ -762,6 +782,59 @@ InitPlan(CmdType operation, Query *parseTree, Plan *plan, EState *estate) return tupType; } +/* + * Initialize ResultRelInfo data for one result relation + */ +static void +initResultRelInfo(ResultRelInfo *resultRelInfo, + Index resultRelationIndex, + List *rangeTable, + CmdType operation) +{ + Oid resultRelationOid; + Relation resultRelationDesc; + + resultRelationOid = getrelid(resultRelationIndex, rangeTable); + resultRelationDesc = heap_open(resultRelationOid, RowExclusiveLock); + + switch (resultRelationDesc->rd_rel->relkind) + { + case RELKIND_SEQUENCE: + elog(ERROR, "You can't change sequence relation %s", + RelationGetRelationName(resultRelationDesc)); + break; + case RELKIND_TOASTVALUE: + elog(ERROR, "You can't change toast relation %s", + RelationGetRelationName(resultRelationDesc)); + break; + case RELKIND_VIEW: + elog(ERROR, "You can't change view relation %s", + RelationGetRelationName(resultRelationDesc)); + break; + } + + MemSet(resultRelInfo, 0, sizeof(ResultRelInfo)); + resultRelInfo->type = T_ResultRelInfo; + resultRelInfo->ri_RangeTableIndex = resultRelationIndex; + resultRelInfo->ri_RelationDesc = resultRelationDesc; + resultRelInfo->ri_NumIndices = 0; + resultRelInfo->ri_IndexRelationDescs = NULL; + resultRelInfo->ri_IndexRelationInfo = NULL; + resultRelInfo->ri_ConstraintExprs = NULL; + resultRelInfo->ri_junkFilter = NULL; + + /* + * If there are indices on the result relation, open them and save + * descriptors in the result relation info, so that we can add new + * index entries for the tuples we add/update. We need not do + * this for a DELETE, however, since deletion doesn't affect + * indexes. + */ + if (resultRelationDesc->rd_rel->relhasindex && + operation != CMD_DELETE) + ExecOpenIndices(resultRelInfo); +} + /* ---------------------------------------------------------------- * EndPlan * @@ -771,7 +844,8 @@ InitPlan(CmdType operation, Query *parseTree, Plan *plan, EState *estate) static void EndPlan(Plan *plan, EState *estate) { - RelationInfo *resultRelationInfo; + ResultRelInfo *resultRelInfo; + int i; List *l; /* @@ -792,16 +866,16 @@ EndPlan(Plan *plan, EState *estate) estate->es_tupleTable = NULL; /* - * close the result relation if necessary, but hold lock on it - * until xact commit. NB: must not do this till after ExecEndNode(), - * see nodeAppend.c ... + * close the result relation(s) if any, but hold locks + * until xact commit. */ - resultRelationInfo = estate->es_result_relation_info; - if (resultRelationInfo != NULL) + resultRelInfo = estate->es_result_relations; + for (i = estate->es_num_result_relations; i > 0; i--) { - heap_close(resultRelationInfo->ri_RelationDesc, NoLock); - /* close indices on the result relation, too */ - ExecCloseIndices(resultRelationInfo); + /* Close indices and then the relation itself */ + ExecCloseIndices(resultRelInfo); + heap_close(resultRelInfo->ri_RelationDesc, NoLock); + resultRelInfo++; } /* @@ -1116,7 +1190,7 @@ ExecAppend(TupleTableSlot *slot, EState *estate) { HeapTuple tuple; - RelationInfo *resultRelationInfo; + ResultRelInfo *resultRelInfo; Relation resultRelationDesc; int numIndices; Oid newId; @@ -1127,10 +1201,10 @@ ExecAppend(TupleTableSlot *slot, tuple = slot->val; /* - * get information on the result relation + * get information on the (current) result relation */ - resultRelationInfo = estate->es_result_relation_info; - resultRelationDesc = resultRelationInfo->ri_RelationDesc; + resultRelInfo = estate->es_result_relation_info; + resultRelationDesc = resultRelInfo->ri_RelationDesc; /* BEFORE ROW INSERT Triggers */ if (resultRelationDesc->trigdesc && @@ -1154,9 +1228,8 @@ ExecAppend(TupleTableSlot *slot, /* * Check the constraints of the tuple */ - if (resultRelationDesc->rd_att->constr) - ExecConstraints("ExecAppend", resultRelationDesc, slot, estate); + ExecConstraints("ExecAppend", resultRelInfo, slot, estate); /* * insert the tuple @@ -1174,7 +1247,7 @@ ExecAppend(TupleTableSlot *slot, * the tupleid of the new tuple is placed in the new tuple's t_ctid * field. */ - numIndices = resultRelationInfo->ri_NumIndices; + numIndices = resultRelInfo->ri_NumIndices; if (numIndices > 0) ExecInsertIndexTuples(slot, &(tuple->t_self), estate, false); @@ -1195,16 +1268,16 @@ ExecDelete(TupleTableSlot *slot, ItemPointer tupleid, EState *estate) { - RelationInfo *resultRelationInfo; + ResultRelInfo *resultRelInfo; Relation resultRelationDesc; ItemPointerData ctid; int result; /* - * get the result relation information + * get information on the (current) result relation */ - resultRelationInfo = estate->es_result_relation_info; - resultRelationDesc = resultRelationInfo->ri_RelationDesc; + resultRelInfo = estate->es_result_relation_info; + resultRelationDesc = resultRelInfo->ri_RelationDesc; /* BEFORE ROW DELETE Triggers */ if (resultRelationDesc->trigdesc && @@ -1237,7 +1310,7 @@ ldelete:; else if (!(ItemPointerEquals(tupleid, &ctid))) { TupleTableSlot *epqslot = EvalPlanQual(estate, - resultRelationInfo->ri_RangeTableIndex, &ctid); + resultRelInfo->ri_RangeTableIndex, &ctid); if (!TupIsNull(epqslot)) { @@ -1287,7 +1360,7 @@ ExecReplace(TupleTableSlot *slot, EState *estate) { HeapTuple tuple; - RelationInfo *resultRelationInfo; + ResultRelInfo *resultRelInfo; Relation resultRelationDesc; ItemPointerData ctid; int result; @@ -1308,10 +1381,10 @@ ExecReplace(TupleTableSlot *slot, tuple = slot->val; /* - * get the result relation information + * get information on the (current) result relation */ - resultRelationInfo = estate->es_result_relation_info; - resultRelationDesc = resultRelationInfo->ri_RelationDesc; + resultRelInfo = estate->es_result_relation_info; + resultRelationDesc = resultRelInfo->ri_RelationDesc; /* BEFORE ROW UPDATE Triggers */ if (resultRelationDesc->trigdesc && @@ -1335,9 +1408,8 @@ ExecReplace(TupleTableSlot *slot, /* * Check the constraints of the tuple */ - if (resultRelationDesc->rd_att->constr) - ExecConstraints("ExecReplace", resultRelationDesc, slot, estate); + ExecConstraints("ExecReplace", resultRelInfo, slot, estate); /* * replace the heap tuple @@ -1358,7 +1430,7 @@ lreplace:; else if (!(ItemPointerEquals(tupleid, &ctid))) { TupleTableSlot *epqslot = EvalPlanQual(estate, - resultRelationInfo->ri_RangeTableIndex, &ctid); + resultRelInfo->ri_RangeTableIndex, &ctid); if (!TupIsNull(epqslot)) { @@ -1396,7 +1468,7 @@ lreplace:; * there. */ - numIndices = resultRelationInfo->ri_NumIndices; + numIndices = resultRelInfo->ri_NumIndices; if (numIndices > 0) ExecInsertIndexTuples(slot, &(tuple->t_self), estate, true); @@ -1406,8 +1478,10 @@ lreplace:; } static char * -ExecRelCheck(Relation rel, TupleTableSlot *slot, EState *estate) +ExecRelCheck(ResultRelInfo *resultRelInfo, + TupleTableSlot *slot, EState *estate) { + Relation rel = resultRelInfo->ri_RelationDesc; int ncheck = rel->rd_att->constr->num_check; ConstrCheck *check = rel->rd_att->constr->check; ExprContext *econtext; @@ -1415,6 +1489,24 @@ ExecRelCheck(Relation rel, TupleTableSlot *slot, EState *estate) List *qual; int i; + /* + * If first time through for this result relation, build expression + * nodetrees for rel's constraint expressions. Keep them in the + * per-query memory context so they'll survive throughout the query. + */ + if (resultRelInfo->ri_ConstraintExprs == NULL) + { + oldContext = MemoryContextSwitchTo(estate->es_query_cxt); + resultRelInfo->ri_ConstraintExprs = + (List **) palloc(ncheck * sizeof(List *)); + for (i = 0; i < ncheck; i++) + { + qual = (List *) stringToNode(check[i].ccbin); + resultRelInfo->ri_ConstraintExprs[i] = qual; + } + MemoryContextSwitchTo(oldContext); + } + /* * We will use the EState's per-tuple context for evaluating constraint * expressions. Create it if it's not already there; if it is, reset it @@ -1431,58 +1523,13 @@ ExecRelCheck(Relation rel, TupleTableSlot *slot, EState *estate) else ResetExprContext(econtext); - /* - * If first time through for current result relation, set up econtext's - * range table to refer to result rel, and build expression nodetrees - * for rel's constraint expressions. All this stuff is kept in the - * per-query memory context so it will still be here next time through. - * - * NOTE: if there are multiple result relations (eg, due to inheritance) - * then we leak storage for prior rel's expressions and rangetable. - * This should not be a big problem as long as result rels are processed - * sequentially within a command. - */ - if (econtext->ecxt_range_table == NIL || - getrelid(1, econtext->ecxt_range_table) != RelationGetRelid(rel)) - { - RangeTblEntry *rte; - - /* - * Make sure expressions, etc are placed in appropriate context. - */ - oldContext = MemoryContextSwitchTo(estate->es_query_cxt); - - rte = makeNode(RangeTblEntry); - - rte->relname = RelationGetRelationName(rel); - rte->relid = RelationGetRelid(rel); - rte->eref = makeNode(Attr); - rte->eref->relname = rte->relname; - /* other fields won't be used, leave them zero */ - - /* Set up single-entry range table */ - econtext->ecxt_range_table = makeList1(rte); - - estate->es_result_relation_constraints = - (List **) palloc(ncheck * sizeof(List *)); - - for (i = 0; i < ncheck; i++) - { - qual = (List *) stringToNode(check[i].ccbin); - estate->es_result_relation_constraints[i] = qual; - } - - /* Done with building long-lived items */ - MemoryContextSwitchTo(oldContext); - } - /* Arrange for econtext's scan tuple to be the tuple under test */ econtext->ecxt_scantuple = slot; /* And evaluate the constraints */ for (i = 0; i < ncheck; i++) { - qual = estate->es_result_relation_constraints[i]; + qual = resultRelInfo->ri_ConstraintExprs[i]; /* * NOTE: SQL92 specifies that a NULL result from a constraint @@ -1498,9 +1545,10 @@ ExecRelCheck(Relation rel, TupleTableSlot *slot, EState *estate) } void -ExecConstraints(char *caller, Relation rel, +ExecConstraints(char *caller, ResultRelInfo *resultRelInfo, TupleTableSlot *slot, EState *estate) { + Relation rel = resultRelInfo->ri_RelationDesc; HeapTuple tuple = slot->val; TupleConstr *constr = rel->rd_att->constr; @@ -1524,7 +1572,7 @@ ExecConstraints(char *caller, Relation rel, { char *failed; - if ((failed = ExecRelCheck(rel, slot, estate)) != NULL) + if ((failed = ExecRelCheck(resultRelInfo, slot, estate)) != NULL) elog(ERROR, "%s: rejected due to CHECK constraint %s", caller, failed); } diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c index 622ea2ef82c..beeff0db331 100644 --- a/src/backend/executor/execQual.c +++ b/src/backend/executor/execQual.c @@ -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); diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c index 408716abf83..65eef4141f4 100644 --- a/src/backend/executor/execTuples.c +++ b/src/backend/executor/execTuples.c @@ -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: { diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c index 4eff4a07bb2..ecdda594188 100644 --- a/src/backend/executor/execUtils.c +++ b/src/backend/executor/execUtils.c @@ -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); /* diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c index e5a5e55ef8d..ca9c48e294d 100644 --- a/src/backend/executor/functions.c +++ b/src/backend/executor/functions.c @@ -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; } diff --git a/src/backend/executor/nodeAppend.c b/src/backend/executor/nodeAppend.c index 81eeb1e8b0b..e47ec5756cb 100644 --- a/src/backend/executor/nodeAppend.c +++ b/src/backend/executor/nodeAppend.c @@ -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) { diff --git a/src/backend/executor/nodeSeqscan.c b/src/backend/executor/nodeSeqscan.c index b953dcd3697..56ba1375feb 100644 --- a/src/backend/executor/nodeSeqscan.c +++ b/src/backend/executor/nodeSeqscan.c @@ -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 */ diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index 93866787f37..3490ef80624 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -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; diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index 7ed95db9679..8fb81a45c0b 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -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; diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index 58ab6896889..d7137f10ace 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -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; diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c index 354ac8d5839..6a6dce48bfb 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -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) diff --git a/src/backend/optimizer/README b/src/backend/optimizer/README index f0113dfaf50..e60f1457208 100644 --- a/src/backend/optimizer/README +++ b/src/backend/optimizer/README @@ -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 diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c index 7e017a746f1..12fc576612f 100644 --- a/src/backend/optimizer/path/allpaths.c +++ b/src/backend/optimizer/path/allpaths.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/path/allpaths.c,v 1.66 2000/10/05 19:11:28 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/path/allpaths.c,v 1.67 2000/11/12 00:36:58 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -21,6 +21,7 @@ #include "optimizer/paths.h" #include "optimizer/plancat.h" #include "optimizer/planner.h" +#include "optimizer/prep.h" #include "parser/parsetree.h" @@ -28,7 +29,12 @@ bool enable_geqo = true; int geqo_rels = DEFAULT_GEQO_RELS; -static void set_base_rel_pathlist(Query *root); +static void set_base_rel_pathlists(Query *root); +static void set_plain_rel_pathlist(Query *root, RelOptInfo *rel, + RangeTblEntry *rte); +static void set_inherited_rel_pathlist(Query *root, RelOptInfo *rel, + RangeTblEntry *rte, + List *inheritlist); static RelOptInfo *make_one_rel_by_joins(Query *root, int levels_needed, List *initial_rels); @@ -51,7 +57,7 @@ make_one_rel(Query *root) /* * Generate access paths for the base rels. */ - set_base_rel_pathlist(root); + set_base_rel_pathlists(root); /* * Generate access paths for the entire join tree. @@ -69,23 +75,26 @@ make_one_rel(Query *root) } /* - * set_base_rel_pathlist + * set_base_rel_pathlists * Finds all paths available for scanning each base-relation entry. * Sequential scan and any available indices are considered. * Each useful path is attached to its relation's 'pathlist' field. */ static void -set_base_rel_pathlist(Query *root) +set_base_rel_pathlists(Query *root) { List *rellist; foreach(rellist, root->base_rel_list) { RelOptInfo *rel = (RelOptInfo *) lfirst(rellist); + Index rti; RangeTblEntry *rte; + List *inheritlist; Assert(length(rel->relids) == 1); /* better be base rel */ - rte = rt_fetch(lfirsti(rel->relids), root->rtable); + rti = lfirsti(rel->relids); + rte = rt_fetch(rti, root->rtable); if (rel->issubquery) { @@ -109,47 +118,163 @@ set_base_rel_pathlist(Query *root) /* Generate appropriate path */ add_path(rel, create_subqueryscan_path(rel)); + + /* Select cheapest path (pretty easy in this case...) */ + set_cheapest(rel); + } + else if ((inheritlist = expand_inherted_rtentry(root, rti)) != NIL) + { + /* Relation is root of an inheritance tree, process specially */ + set_inherited_rel_pathlist(root, rel, rte, inheritlist); } else { /* Plain relation */ - List *indices = find_secondary_indexes(rte->relid); - - /* Mark rel with estimated output rows, width, etc */ - set_baserel_size_estimates(root, rel); - - /* - * Generate paths and add them to the rel's pathlist. - * - * Note: add_path() will discard any paths that are dominated by - * another available path, keeping only those paths that are - * superior along at least one dimension of cost or sortedness. - */ - - /* Consider sequential scan */ - add_path(rel, create_seqscan_path(rel)); - - /* Consider TID scans */ - create_tidscan_paths(root, rel); - - /* Consider index paths for both simple and OR index clauses */ - create_index_paths(root, rel, indices, - rel->baserestrictinfo, - rel->joininfo); - - /* - * Note: create_or_index_paths depends on create_index_paths to - * have marked OR restriction clauses with relevant indices; this - * is why it doesn't need to be given the list of indices. - */ - create_or_index_paths(root, rel, rel->baserestrictinfo); + set_plain_rel_pathlist(root, rel, rte); } - - /* Now find the cheapest of the paths for this rel */ - set_cheapest(rel); } } +/* + * set_plain_rel_pathlist + * Build access paths for a plain relation (no subquery, no inheritance) + */ +static void +set_plain_rel_pathlist(Query *root, RelOptInfo *rel, RangeTblEntry *rte) +{ + List *indices = find_secondary_indexes(rte->relid); + + /* Mark rel with estimated output rows, width, etc */ + set_baserel_size_estimates(root, rel); + + /* + * Generate paths and add them to the rel's pathlist. + * + * Note: add_path() will discard any paths that are dominated by + * another available path, keeping only those paths that are + * superior along at least one dimension of cost or sortedness. + */ + + /* Consider sequential scan */ + add_path(rel, create_seqscan_path(rel)); + + /* Consider TID scans */ + create_tidscan_paths(root, rel); + + /* Consider index paths for both simple and OR index clauses */ + create_index_paths(root, rel, indices, + rel->baserestrictinfo, + rel->joininfo); + + /* + * Note: create_or_index_paths depends on create_index_paths to + * have marked OR restriction clauses with relevant indices; this + * is why it doesn't need to be given the list of indices. + */ + create_or_index_paths(root, rel, rel->baserestrictinfo); + + /* Now find the cheapest of the paths for this rel */ + set_cheapest(rel); +} + +/* + * set_inherited_rel_pathlist + * Build access paths for a inheritance tree rooted at rel + * + * inheritlist is a list of RT indexes of all tables in the inheritance tree, + * including the parent itself. Note we will not come here unless there's + * at least one child in addition to the parent. + */ +static void +set_inherited_rel_pathlist(Query *root, RelOptInfo *rel, RangeTblEntry *rte, + List *inheritlist) +{ + int parentRTindex = lfirsti(rel->relids); + Oid parentOID = rte->relid; + List *subpaths = NIL; + List *il; + + /* + * XXX for now, can't handle inherited expansion of FOR UPDATE; + * can we do better? + */ + if (intMember(parentRTindex, root->rowMarks)) + elog(ERROR, "SELECT FOR UPDATE is not supported for inherit queries"); + + /* + * Recompute size estimates for whole inheritance tree + */ + rel->rows = 0; + rel->width = 0; + + /* + * Generate access paths for each table in the tree (parent AND children), + * and pick the cheapest path for each table. + */ + foreach(il, inheritlist) + { + int childRTindex = lfirsti(il); + RangeTblEntry *childrte; + Oid childOID; + RelOptInfo *childrel; + + childrte = rt_fetch(childRTindex, root->rtable); + childOID = childrte->relid; + + /* + * Make a RelOptInfo for the child so we can do planning. Do NOT + * attach the RelOptInfo to the query's base_rel_list, however. + * + * NOTE: when childRTindex == parentRTindex, we create a second + * RelOptInfo for the same relation. This RelOptInfo will represent + * the parent table alone, whereas the original RelOptInfo represents + * the union of the inheritance tree members. + */ + childrel = make_base_rel(root, childRTindex); + + /* + * Copy the parent's targetlist and restriction quals to the child, + * with attribute-number adjustment if needed. We don't bother + * to copy the join quals, since we can't do any joining here. + */ + childrel->targetlist = (List *) + adjust_inherited_attrs((Node *) rel->targetlist, + parentRTindex, + parentOID, + childRTindex, + childOID); + childrel->baserestrictinfo = (List *) + adjust_inherited_attrs((Node *) rel->baserestrictinfo, + parentRTindex, + parentOID, + childRTindex, + childOID); + childrel->baserestrictcost = rel->baserestrictcost; + + /* + * Now compute child access paths, and save the cheapest. + */ + set_plain_rel_pathlist(root, childrel, childrte); + + subpaths = lappend(subpaths, childrel->cheapest_total_path); + + /* Also update total size estimates */ + rel->rows += childrel->rows; + if (childrel->width > rel->width) + rel->width = childrel->width; + } + + /* + * Finally, build Append path and install it as the only access + * path for the parent rel. + */ + add_path(rel, (Path *) create_append_path(rel, subpaths)); + + /* Select cheapest path (pretty easy in this case...) */ + set_cheapest(rel); +} + + /* * make_fromexpr_rel * Build access paths for a FromExpr jointree node. diff --git a/src/backend/optimizer/path/pathkeys.c b/src/backend/optimizer/path/pathkeys.c index 820492bd186..f94d2e4037b 100644 --- a/src/backend/optimizer/path/pathkeys.c +++ b/src/backend/optimizer/path/pathkeys.c @@ -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 diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c index a865da61b92..4069ed66e58 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -10,7 +10,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.99 2000/10/26 21:36:09 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.100 2000/11/12 00:36:58 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -32,35 +32,38 @@ #include "utils/syscache.h" -static List *switch_outer(List *clauses); -static Scan *create_scan_node(Query *root, Path *best_path, List *tlist); -static Join *create_join_node(Query *root, JoinPath *best_path, List *tlist); -static SeqScan *create_seqscan_node(Path *best_path, List *tlist, +static Scan *create_scan_plan(Query *root, Path *best_path); +static Join *create_join_plan(Query *root, JoinPath *best_path); +static Append *create_append_plan(Query *root, AppendPath *best_path); +static SeqScan *create_seqscan_plan(Path *best_path, List *tlist, List *scan_clauses); -static IndexScan *create_indexscan_node(Query *root, IndexPath *best_path, +static IndexScan *create_indexscan_plan(Query *root, IndexPath *best_path, List *tlist, List *scan_clauses); -static TidScan *create_tidscan_node(TidPath *best_path, List *tlist, +static TidScan *create_tidscan_plan(TidPath *best_path, List *tlist, List *scan_clauses); -static SubqueryScan *create_subqueryscan_node(Path *best_path, +static SubqueryScan *create_subqueryscan_plan(Path *best_path, List *tlist, List *scan_clauses); -static NestLoop *create_nestloop_node(NestPath *best_path, List *tlist, +static NestLoop *create_nestloop_plan(NestPath *best_path, List *tlist, List *joinclauses, List *otherclauses, - Plan *outer_node, List *outer_tlist, - Plan *inner_node, List *inner_tlist); -static MergeJoin *create_mergejoin_node(MergePath *best_path, List *tlist, + Plan *outer_plan, List *outer_tlist, + Plan *inner_plan, List *inner_tlist); +static MergeJoin *create_mergejoin_plan(MergePath *best_path, List *tlist, List *joinclauses, List *otherclauses, - Plan *outer_node, List *outer_tlist, - Plan *inner_node, List *inner_tlist); -static HashJoin *create_hashjoin_node(HashPath *best_path, List *tlist, + Plan *outer_plan, List *outer_tlist, + Plan *inner_plan, List *inner_tlist); +static HashJoin *create_hashjoin_plan(HashPath *best_path, List *tlist, List *joinclauses, List *otherclauses, - Plan *outer_node, List *outer_tlist, - Plan *inner_node, List *inner_tlist); + Plan *outer_plan, List *outer_tlist, + Plan *inner_plan, List *inner_tlist); static List *fix_indxqual_references(List *indexquals, IndexPath *index_path); static List *fix_indxqual_sublist(List *indexqual, int baserelid, Oid relam, Form_pg_index index); static Node *fix_indxqual_operand(Node *node, int baserelid, Form_pg_index index, Oid *opclass); +static List *switch_outer(List *clauses); +static void copy_path_costsize(Plan *dest, Path *src); +static void copy_plan_costsize(Plan *dest, Plan *src); static SeqScan *make_seqscan(List *qptlist, List *qpqual, Index scanrelid); static IndexScan *make_indexscan(List *qptlist, List *qpqual, Index scanrelid, List *indxid, List *indxqual, @@ -83,7 +86,6 @@ static MergeJoin *make_mergejoin(List *tlist, List *mergeclauses, Plan *lefttree, Plan *righttree, JoinType jointype); -static void copy_path_costsize(Plan *dest, Path *src); /* * create_plan @@ -98,13 +100,12 @@ static void copy_path_costsize(Plan *dest, Path *src); * * best_path is the best access path * - * Returns the access plan. + * Returns a Plan tree. */ Plan * create_plan(Query *root, Path *best_path) { - List *tlist = best_path->parent->targetlist; - Plan *plan_node = (Plan *) NULL; + Plan *plan; switch (best_path->pathtype) { @@ -112,18 +113,22 @@ create_plan(Query *root, Path *best_path) case T_SeqScan: case T_TidScan: case T_SubqueryScan: - plan_node = (Plan *) create_scan_node(root, best_path, tlist); + plan = (Plan *) create_scan_plan(root, best_path); break; case T_HashJoin: case T_MergeJoin: case T_NestLoop: - plan_node = (Plan *) create_join_node(root, - (JoinPath *) best_path, - tlist); + plan = (Plan *) create_join_plan(root, + (JoinPath *) best_path); + break; + case T_Append: + plan = (Plan *) create_append_plan(root, + (AppendPath *) best_path); break; default: elog(ERROR, "create_plan: unknown pathtype %d", best_path->pathtype); + plan = NULL; /* keep compiler quiet */ break; } @@ -131,30 +136,29 @@ create_plan(Query *root, Path *best_path) /* sort clauses by cost/(1-selectivity) -- JMH 2/26/92 */ if (XfuncMode != XFUNC_OFF) { - set_qpqual((Plan) plan_node, - lisp_qsort(get_qpqual((Plan) plan_node), + set_qpqual((Plan) plan, + lisp_qsort(get_qpqual((Plan) plan), xfunc_clause_compare)); if (XfuncMode != XFUNC_NOR) /* sort the disjuncts within each clause by cost -- JMH 3/4/92 */ - xfunc_disjunct_sort(plan_node->qpqual); + xfunc_disjunct_sort(plan->qpqual); } #endif - return plan_node; + return plan; } /* - * create_scan_node - * Create a scan path for the parent relation of 'best_path'. + * create_scan_plan + * Create a scan plan for the parent relation of 'best_path'. * - * tlist is the targetlist for the base relation scanned by 'best_path' - * - * Returns the scan node. + * Returns a Plan node. */ static Scan * -create_scan_node(Query *root, Path *best_path, List *tlist) +create_scan_plan(Query *root, Path *best_path) { - Scan *node = NULL; + Scan *plan; + List *tlist = best_path->parent->targetlist; List *scan_clauses; /* @@ -166,65 +170,64 @@ create_scan_node(Query *root, Path *best_path, List *tlist) switch (best_path->pathtype) { case T_SeqScan: - node = (Scan *) create_seqscan_node(best_path, + plan = (Scan *) create_seqscan_plan(best_path, tlist, scan_clauses); break; case T_IndexScan: - node = (Scan *) create_indexscan_node(root, + plan = (Scan *) create_indexscan_plan(root, (IndexPath *) best_path, tlist, scan_clauses); break; case T_TidScan: - node = (Scan *) create_tidscan_node((TidPath *) best_path, + plan = (Scan *) create_tidscan_plan((TidPath *) best_path, tlist, scan_clauses); break; case T_SubqueryScan: - node = (Scan *) create_subqueryscan_node(best_path, + plan = (Scan *) create_subqueryscan_plan(best_path, tlist, scan_clauses); break; default: - elog(ERROR, "create_scan_node: unknown node type: %d", + elog(ERROR, "create_scan_plan: unknown node type: %d", best_path->pathtype); + plan = NULL; /* keep compiler quiet */ break; } - return node; + return plan; } /* - * create_join_node - * Create a join path for 'best_path' and(recursively) paths for its + * create_join_plan + * Create a join plan for 'best_path' and (recursively) plans for its * inner and outer paths. * - * 'tlist' is the targetlist for the join relation corresponding to - * 'best_path' - * - * Returns the join node. + * Returns a Plan node. */ static Join * -create_join_node(Query *root, JoinPath *best_path, List *tlist) +create_join_plan(Query *root, JoinPath *best_path) { - Plan *outer_node; + List *join_tlist = best_path->path.parent->targetlist; + Plan *outer_plan; List *outer_tlist; - Plan *inner_node; + Plan *inner_plan; List *inner_tlist; List *joinclauses; List *otherclauses; - Join *retval = NULL; + Join *plan; - outer_node = create_plan(root, best_path->outerjoinpath); - outer_tlist = outer_node->targetlist; + outer_plan = create_plan(root, best_path->outerjoinpath); + outer_tlist = outer_plan->targetlist; - inner_node = create_plan(root, best_path->innerjoinpath); - inner_tlist = inner_node->targetlist; + inner_plan = create_plan(root, best_path->innerjoinpath); + inner_tlist = inner_plan->targetlist; if (IS_OUTER_JOIN(best_path->jointype)) { @@ -241,38 +244,40 @@ create_join_node(Query *root, JoinPath *best_path, List *tlist) switch (best_path->path.pathtype) { case T_MergeJoin: - retval = (Join *) create_mergejoin_node((MergePath *) best_path, - tlist, - joinclauses, - otherclauses, - outer_node, - outer_tlist, - inner_node, - inner_tlist); + plan = (Join *) create_mergejoin_plan((MergePath *) best_path, + join_tlist, + joinclauses, + otherclauses, + outer_plan, + outer_tlist, + inner_plan, + inner_tlist); break; case T_HashJoin: - retval = (Join *) create_hashjoin_node((HashPath *) best_path, - tlist, - joinclauses, - otherclauses, - outer_node, - outer_tlist, - inner_node, - inner_tlist); + plan = (Join *) create_hashjoin_plan((HashPath *) best_path, + join_tlist, + joinclauses, + otherclauses, + outer_plan, + outer_tlist, + inner_plan, + inner_tlist); break; case T_NestLoop: - retval = (Join *) create_nestloop_node((NestPath *) best_path, - tlist, - joinclauses, - otherclauses, - outer_node, - outer_tlist, - inner_node, - inner_tlist); + plan = (Join *) create_nestloop_plan((NestPath *) best_path, + join_tlist, + joinclauses, + otherclauses, + outer_plan, + outer_tlist, + inner_plan, + inner_tlist); break; default: - elog(ERROR, "create_join_node: unknown node type: %d", + elog(ERROR, "create_join_plan: unknown node type: %d", best_path->path.pathtype); + plan = NULL; /* keep compiler quiet */ + break; } #ifdef NOT_USED @@ -283,14 +288,42 @@ create_join_node(Query *root, JoinPath *best_path, List *tlist) * JMH, 6/15/92 */ if (get_loc_restrictinfo(best_path) != NIL) - set_qpqual((Plan) retval, - nconc(get_qpqual((Plan) retval), + set_qpqual((Plan) plan, + nconc(get_qpqual((Plan) plan), get_actual_clauses(get_loc_restrictinfo(best_path)))); #endif - return retval; + return plan; } +/* + * create_append_plan + * Create an Append plan for 'best_path' and (recursively) plans + * for its subpaths. + * + * Returns a Plan node. + */ +static Append * +create_append_plan(Query *root, AppendPath *best_path) +{ + Append *plan; + List *tlist = best_path->path.parent->targetlist; + List *subplans = NIL; + List *subpaths; + + foreach(subpaths, best_path->subpaths) + { + Path *subpath = (Path *) lfirst(subpaths); + + subplans = lappend(subplans, create_plan(root, subpath)); + } + + plan = make_append(subplans, false, tlist); + + return plan; +} + + /***************************************************************************** * * BASE-RELATION SCAN METHODS @@ -299,14 +332,14 @@ create_join_node(Query *root, JoinPath *best_path, List *tlist) /* - * create_seqscan_node - * Returns a seqscan node for the base relation scanned by 'best_path' + * create_seqscan_plan + * Returns a seqscan plan for the base relation scanned by 'best_path' * with restriction clauses 'scan_clauses' and targetlist 'tlist'. */ static SeqScan * -create_seqscan_node(Path *best_path, List *tlist, List *scan_clauses) +create_seqscan_plan(Path *best_path, List *tlist, List *scan_clauses) { - SeqScan *scan_node; + SeqScan *scan_plan; Index scan_relid; /* there should be exactly one base rel involved... */ @@ -315,18 +348,18 @@ create_seqscan_node(Path *best_path, List *tlist, List *scan_clauses) scan_relid = (Index) lfirsti(best_path->parent->relids); - scan_node = make_seqscan(tlist, + scan_plan = make_seqscan(tlist, scan_clauses, scan_relid); - copy_path_costsize(&scan_node->plan, best_path); + copy_path_costsize(&scan_plan->plan, best_path); - return scan_node; + return scan_plan; } /* - * create_indexscan_node - * Returns a indexscan node for the base relation scanned by 'best_path' + * create_indexscan_plan + * Returns a indexscan plan for the base relation scanned by 'best_path' * with restriction clauses 'scan_clauses' and targetlist 'tlist'. * * The indexqual of the path contains a sublist of implicitly-ANDed qual @@ -338,7 +371,7 @@ create_seqscan_node(Path *best_path, List *tlist, List *scan_clauses) * scan. */ static IndexScan * -create_indexscan_node(Query *root, +create_indexscan_plan(Query *root, IndexPath *best_path, List *tlist, List *scan_clauses) @@ -348,7 +381,7 @@ create_indexscan_node(Query *root, List *qpqual; List *fixed_indxqual; List *ixid; - IndexScan *scan_node; + IndexScan *scan_plan; bool lossy = false; /* there should be exactly one base rel involved... */ @@ -433,7 +466,7 @@ create_indexscan_node(Query *root, */ fixed_indxqual = fix_indxqual_references(indxqual, best_path); - scan_node = make_indexscan(tlist, + scan_plan = make_indexscan(tlist, qpqual, baserelid, best_path->indexid, @@ -441,22 +474,22 @@ create_indexscan_node(Query *root, indxqual, best_path->indexscandir); - copy_path_costsize(&scan_node->scan.plan, &best_path->path); + copy_path_costsize(&scan_plan->scan.plan, &best_path->path); /* use the indexscan-specific rows estimate, not the parent rel's */ - scan_node->scan.plan.plan_rows = best_path->rows; + scan_plan->scan.plan.plan_rows = best_path->rows; - return scan_node; + return scan_plan; } /* - * create_tidscan_node - * Returns a tidscan node for the base relation scanned by 'best_path' + * create_tidscan_plan + * Returns a tidscan plan for the base relation scanned by 'best_path' * with restriction clauses 'scan_clauses' and targetlist 'tlist'. */ static TidScan * -create_tidscan_node(TidPath *best_path, List *tlist, List *scan_clauses) +create_tidscan_plan(TidPath *best_path, List *tlist, List *scan_clauses) { - TidScan *scan_node; + TidScan *scan_plan; Index scan_relid; /* there should be exactly one base rel involved... */ @@ -465,28 +498,28 @@ create_tidscan_node(TidPath *best_path, List *tlist, List *scan_clauses) scan_relid = (Index) lfirsti(best_path->path.parent->relids); - scan_node = make_tidscan(tlist, + scan_plan = make_tidscan(tlist, scan_clauses, scan_relid, best_path->tideval); if (best_path->unjoined_relids) - scan_node->needRescan = true; + scan_plan->needRescan = true; - copy_path_costsize(&scan_node->scan.plan, &best_path->path); + copy_path_costsize(&scan_plan->scan.plan, &best_path->path); - return scan_node; + return scan_plan; } /* - * create_subqueryscan_node - * Returns a subqueryscan node for the base relation scanned by 'best_path' + * create_subqueryscan_plan + * Returns a subqueryscan plan for the base relation scanned by 'best_path' * with restriction clauses 'scan_clauses' and targetlist 'tlist'. */ static SubqueryScan * -create_subqueryscan_node(Path *best_path, List *tlist, List *scan_clauses) +create_subqueryscan_plan(Path *best_path, List *tlist, List *scan_clauses) { - SubqueryScan *scan_node; + SubqueryScan *scan_plan; Index scan_relid; /* there should be exactly one base rel involved... */ @@ -496,14 +529,12 @@ create_subqueryscan_node(Path *best_path, List *tlist, List *scan_clauses) scan_relid = (Index) lfirsti(best_path->parent->relids); - scan_node = make_subqueryscan(tlist, + scan_plan = make_subqueryscan(tlist, scan_clauses, scan_relid, best_path->parent->subplan); - copy_path_costsize(&scan_node->scan.plan, best_path); - - return scan_node; + return scan_plan; } /***************************************************************************** @@ -528,18 +559,18 @@ create_subqueryscan_node(Path *best_path, List *tlist, List *scan_clauses) *****************************************************************************/ static NestLoop * -create_nestloop_node(NestPath *best_path, +create_nestloop_plan(NestPath *best_path, List *tlist, List *joinclauses, List *otherclauses, - Plan *outer_node, + Plan *outer_plan, List *outer_tlist, - Plan *inner_node, + Plan *inner_plan, List *inner_tlist) { - NestLoop *join_node; + NestLoop *join_plan; - if (IsA(inner_node, IndexScan)) + if (IsA(inner_plan, IndexScan)) { /* @@ -563,7 +594,7 @@ create_nestloop_node(NestPath *best_path, * and therefore has not itself done join_references renumbering * of the vars in its quals. */ - IndexScan *innerscan = (IndexScan *) inner_node; + IndexScan *innerscan = (IndexScan *) inner_plan; List *indxqualorig = innerscan->indxqualorig; /* No work needed if indxqual refers only to its own relation... */ @@ -591,23 +622,23 @@ create_nestloop_node(NestPath *best_path, NIL, innerrel); /* fix the inner qpqual too, if it has join clauses */ - if (NumRelids((Node *) inner_node->qual) > 1) - inner_node->qual = join_references(inner_node->qual, + if (NumRelids((Node *) inner_plan->qual) > 1) + inner_plan->qual = join_references(inner_plan->qual, outer_tlist, NIL, innerrel); } } - else if (IsA(inner_node, TidScan)) + else if (IsA(inner_plan, TidScan)) { - TidScan *innerscan = (TidScan *) inner_node; + TidScan *innerscan = (TidScan *) inner_plan; innerscan->tideval = join_references(innerscan->tideval, outer_tlist, inner_tlist, innerscan->scan.scanrelid); } - else if (IsA_Join(inner_node)) + else if (IsA_Join(inner_plan)) { /* @@ -617,8 +648,8 @@ create_nestloop_node(NestPath *best_path, * join --- how can we estimate whether this is a good thing to * do? */ - inner_node = (Plan *) make_material(inner_tlist, - inner_node); + inner_plan = (Plan *) make_material(inner_tlist, + inner_plan); } /* @@ -633,30 +664,30 @@ create_nestloop_node(NestPath *best_path, inner_tlist, (Index) 0); - join_node = make_nestloop(tlist, + join_plan = make_nestloop(tlist, joinclauses, otherclauses, - outer_node, - inner_node, + outer_plan, + inner_plan, best_path->jointype); - copy_path_costsize(&join_node->join.plan, &best_path->path); + copy_path_costsize(&join_plan->join.plan, &best_path->path); - return join_node; + return join_plan; } static MergeJoin * -create_mergejoin_node(MergePath *best_path, +create_mergejoin_plan(MergePath *best_path, List *tlist, List *joinclauses, List *otherclauses, - Plan *outer_node, + Plan *outer_plan, List *outer_tlist, - Plan *inner_node, + Plan *inner_plan, List *inner_tlist) { List *mergeclauses; - MergeJoin *join_node; + MergeJoin *join_plan; mergeclauses = get_actual_clauses(best_path->path_mergeclauses); @@ -692,15 +723,15 @@ create_mergejoin_node(MergePath *best_path, * necessary. The sort cost was already accounted for in the path. */ if (best_path->outersortkeys) - outer_node = (Plan *) + outer_plan = (Plan *) make_sort_from_pathkeys(outer_tlist, - outer_node, + outer_plan, best_path->outersortkeys); if (best_path->innersortkeys) - inner_node = (Plan *) + inner_plan = (Plan *) make_sort_from_pathkeys(inner_tlist, - inner_node, + inner_plan, best_path->innersortkeys); /* @@ -723,7 +754,7 @@ create_mergejoin_node(MergePath *best_path, * This check must agree with ExecMarkPos/ExecRestrPos in * executor/execAmi.c! */ - switch (nodeTag(inner_node)) + switch (nodeTag(inner_plan)) { case T_SeqScan: case T_IndexScan: @@ -734,40 +765,40 @@ create_mergejoin_node(MergePath *best_path, default: /* Ooops, need to materialize the inner plan */ - inner_node = (Plan *) make_material(inner_tlist, - inner_node); + inner_plan = (Plan *) make_material(inner_tlist, + inner_plan); break; } /* * Now we can build the mergejoin node. */ - join_node = make_mergejoin(tlist, + join_plan = make_mergejoin(tlist, joinclauses, otherclauses, mergeclauses, - outer_node, - inner_node, + outer_plan, + inner_plan, best_path->jpath.jointype); - copy_path_costsize(&join_node->join.plan, &best_path->jpath.path); + copy_path_costsize(&join_plan->join.plan, &best_path->jpath.path); - return join_node; + return join_plan; } static HashJoin * -create_hashjoin_node(HashPath *best_path, +create_hashjoin_plan(HashPath *best_path, List *tlist, List *joinclauses, List *otherclauses, - Plan *outer_node, + Plan *outer_plan, List *outer_tlist, - Plan *inner_node, + Plan *inner_plan, List *inner_tlist) { List *hashclauses; - HashJoin *join_node; - Hash *hash_node; + HashJoin *join_plan; + Hash *hash_plan; Node *innerhashkey; /* @@ -811,18 +842,18 @@ create_hashjoin_node(HashPath *best_path, /* * Build the hash node and hash join node. */ - hash_node = make_hash(inner_tlist, innerhashkey, inner_node); - join_node = make_hashjoin(tlist, + hash_plan = make_hash(inner_tlist, innerhashkey, inner_plan); + join_plan = make_hashjoin(tlist, joinclauses, otherclauses, hashclauses, - outer_node, - (Plan *) hash_node, + outer_plan, + (Plan *) hash_plan, best_path->jpath.jointype); - copy_path_costsize(&join_node->join.plan, &best_path->jpath.path); + copy_path_costsize(&join_plan->join.plan, &best_path->jpath.path); - return join_node; + return join_plan; } @@ -1106,7 +1137,7 @@ copy_path_costsize(Plan *dest, Path *src) * but it helps produce more reasonable-looking EXPLAIN output. * (Some callers alter the info after copying it.) */ -void +static void copy_plan_costsize(Plan *dest, Plan *src) { if (src) @@ -1128,6 +1159,10 @@ copy_plan_costsize(Plan *dest, Plan *src) /***************************************************************************** * + * PLAN NODE BUILDING ROUTINES + * + * Some of these are exported because they are called to build plan nodes + * in contexts where we're not deriving the plan node from a path node. * *****************************************************************************/ @@ -1212,7 +1247,7 @@ make_subqueryscan(List *qptlist, SubqueryScan *node = makeNode(SubqueryScan); Plan *plan = &node->scan.plan; - /* cost should be inserted by caller */ + copy_plan_costsize(plan, subplan); plan->state = (EState *) NULL; plan->targetlist = qptlist; plan->qual = qpqual; @@ -1225,6 +1260,39 @@ make_subqueryscan(List *qptlist, return node; } +Append * +make_append(List *appendplans, bool isTarget, List *tlist) +{ + Append *node = makeNode(Append); + Plan *plan = &node->plan; + List *subnode; + + /* compute costs from subplan costs */ + plan->startup_cost = 0; + plan->total_cost = 0; + plan->plan_rows = 0; + plan->plan_width = 0; + foreach(subnode, appendplans) + { + Plan *subplan = (Plan *) lfirst(subnode); + + if (subnode == appendplans) /* first node? */ + plan->startup_cost = subplan->startup_cost; + plan->total_cost += subplan->total_cost; + plan->plan_rows += subplan->plan_rows; + if (plan->plan_width < subplan->plan_width) + plan->plan_width = subplan->plan_width; + } + plan->state = (EState *) NULL; + plan->targetlist = tlist; + plan->qual = NIL; + plan->lefttree = NULL; + plan->righttree = NULL; + node->appendplans = appendplans; + node->isTarget = isTarget; + + return node; +} static NestLoop * make_nestloop(List *tlist, diff --git a/src/backend/optimizer/plan/planmain.c b/src/backend/optimizer/plan/planmain.c index a9747b32799..1a923a506ff 100644 --- a/src/backend/optimizer/plan/planmain.c +++ b/src/backend/optimizer/plan/planmain.c @@ -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...) */ diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index 67089e68d29..7a1151f0c9a 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -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. diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c index ea9b902bc5b..ebb09f59393 100644 --- a/src/backend/optimizer/prep/prepunion.c +++ b/src/backend/optimizer/prep/prepunion.c @@ -1,15 +1,20 @@ /*------------------------------------------------------------------------- * * prepunion.c - * Routines to plan set-operation and inheritance queries. The filename - * is a leftover from a time when only UNIONs were handled. + * Routines to plan set-operation queries. The filename is a leftover + * from a time when only UNIONs were implemented. + * + * There is also some code here to support planning of queries that use + * inheritance (SELECT FROM foo*). This no longer has much connection + * to the processing of UNION queries, but it's still here. + * * * Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepunion.c,v 1.55 2000/11/09 02:46:17 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepunion.c,v 1.56 2000/11/12 00:36:59 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -30,13 +35,19 @@ #include "parser/parsetree.h" #include "utils/lsyscache.h" +/* macros borrowed from expression_tree_mutator */ + +#define FLATCOPY(newnode, node, nodetype) \ + ( (newnode) = makeNode(nodetype), \ + memcpy((newnode), (node), sizeof(nodetype)) ) + typedef struct { - Index rt_index; - int sublevels_up; + Index old_rt_index; + Index new_rt_index; Oid old_relid; Oid new_relid; -} fix_parsetree_attnums_context; +} adjust_inherited_attrs_context; static Plan *recurse_set_operations(Node *setOp, Query *parse, List *colTypes, bool junkOK, @@ -53,14 +64,8 @@ static List *generate_setop_tlist(List *colTypes, int flag, List *input_tlist, List *refnames_tlist); static bool tlist_same_datatypes(List *tlist, List *colTypes, bool junkOK); -static void fix_parsetree_attnums(Index rt_index, Oid old_relid, - Oid new_relid, Query *parsetree); -static bool fix_parsetree_attnums_walker(Node *node, - fix_parsetree_attnums_context *context); -static RangeTblEntry *new_rangetable_entry(Oid new_relid, - RangeTblEntry *old_entry); -static Append *make_append(List *appendplans, Index rt_index, - List *inheritrtable, List *tlist); +static Node *adjust_inherited_attrs_mutator(Node *node, + adjust_inherited_attrs_context *context); /* @@ -69,8 +74,8 @@ static Append *make_append(List *appendplans, Index rt_index, * Plans the queries for a tree of set operations (UNION/INTERSECT/EXCEPT) * * This routine only deals with the setOperations tree of the given query. - * Any top-level ORDER BY requested in parse->sortClause will be added on - * back in union_planner. + * Any top-level ORDER BY requested in parse->sortClause will be added + * when we return to grouping_planner. */ Plan * plan_set_operations(Query *parse) @@ -142,7 +147,6 @@ recurse_set_operations(Node *setOp, Query *parse, NIL, rtr->rtindex, subplan); - copy_plan_costsize(plan, subplan); return plan; } else if (IsA(setOp, SetOperationStmt)) @@ -217,8 +221,7 @@ generate_union_plan(SetOperationStmt *op, Query *parse, */ plan = (Plan *) make_append(planlist, - 0, - NIL, + false, generate_setop_tlist(op->colTypes, -1, false, ((Plan *) lfirst(planlist))->targetlist, refnames_tlist)); @@ -269,8 +272,7 @@ generate_nonunion_plan(SetOperationStmt *op, Query *parse, */ plan = (Plan *) make_append(makeList2(lplan, rplan), - 0, - NIL, + false, generate_setop_tlist(op->colTypes, 0, false, lplan->targetlist, refnames_tlist)); @@ -456,132 +458,6 @@ tlist_same_datatypes(List *tlist, List *colTypes, bool junkOK) } -/* - * plan_inherit_queries - * Plans the queries for an inheritance tree rooted at a parent relation. - * - * Inputs: - * root = parent parse tree - * tlist = target list for inheritance subqueries (not same as parent's!) - * rt_index = rangetable index for current inheritance item - * inheritors = list of OIDs of the target rel plus all its descendants - * - * Returns an APPEND node that forms the result of performing the given - * query for each member relation of the inheritance group. - * - * If grouping, aggregation, or sorting is specified in the parent plan, - * the subplans should not do any of those steps --- we must do those - * operations just once above the APPEND node. The given tlist has been - * modified appropriately to remove group/aggregate expressions, but the - * Query node still has the relevant fields set. We remove them in the - * copies used for subplans. - * - * NOTE: this can be invoked recursively if more than one inheritance wildcard - * is present. At each level of recursion, the first wildcard remaining in - * the rangetable is expanded. - * - * NOTE: don't bother optimizing this routine for the case that the target - * rel has no children. We won't get here unless find_inheritable_rt_entry - * found at least two members in the inheritance group, so an APPEND is - * certainly necessary. - */ -Plan * -plan_inherit_queries(Query *root, List *tlist, - Index rt_index, List *inheritors) -{ - RangeTblEntry *rt_entry = rt_fetch(rt_index, root->rtable); - List *union_plans = NIL; - List *union_rtentries = NIL; - List *save_tlist = root->targetList; - double tuple_fraction; - List *i; - - /* - * Avoid making copies of the root's tlist, which we aren't going to - * use anyway (we are going to make copies of the passed tlist, - * instead). This is purely a space-saving hack. Note we restore - * the root's tlist before exiting. - */ - root->targetList = NIL; - - /* - * If we are going to need sorting or grouping at the top level, force - * lower-level planners to assume that all tuples will be retrieved. - */ - if (root->distinctClause || root->sortClause || - root->groupClause || root->hasAggs) - tuple_fraction = 0.0; /* will need all tuples from each subplan */ - else - tuple_fraction = -1.0; /* default behavior is OK (I think) */ - - foreach(i, inheritors) - { - Oid relid = lfirsti(i); - - /* - * Make a modifiable copy of the original query, and replace the - * target rangetable entry in it with a new one identifying this - * child table. The new rtentry is marked inh = false --- this - * is essential to prevent infinite recursion when the subquery - * is rescanned by find_inheritable_rt_entry! - */ - Query *new_root = copyObject(root); - RangeTblEntry *new_rt_entry = new_rangetable_entry(relid, - rt_entry); - - new_rt_entry->inh = false; - rt_store(rt_index, new_root->rtable, new_rt_entry); - - /* - * Insert (a modifiable copy of) the desired simplified tlist into - * the subquery - */ - new_root->targetList = copyObject(tlist); - - /* - * Clear the sorting and grouping qualifications in the subquery, - * so that sorting will only be done once after append - */ - new_root->distinctClause = NIL; - new_root->sortClause = NIL; - new_root->groupClause = NIL; - new_root->havingQual = NULL; - new_root->limitOffset = NULL; /* LIMIT's probably unsafe too */ - new_root->limitCount = NULL; - new_root->hasAggs = false; /* shouldn't be any left ... */ - - /* - * Update attribute numbers in case child has different ordering - * of columns than parent (as can happen after ALTER TABLE). - * - * XXX This is a crock, and it doesn't really work. It'd be better - * to fix ALTER TABLE to preserve consistency of attribute - * numbering. - */ - fix_parsetree_attnums(rt_index, - rt_entry->relid, - relid, - new_root); - - /* - * Plan the subquery by recursively calling union_planner(). - * Add plan and child rtentry to lists for APPEND. - */ - union_plans = lappend(union_plans, - union_planner(new_root, tuple_fraction)); - union_rtentries = lappend(union_rtentries, new_rt_entry); - } - - /* Restore root's tlist */ - root->targetList = save_tlist; - - /* Construct the finished Append plan. */ - return (Plan *) make_append(union_plans, - rt_index, - union_rtentries, - ((Plan *) lfirst(union_plans))->targetlist); -} - /* * find_all_inheritors - * Returns an integer list of relids including the given rel plus @@ -622,200 +498,181 @@ find_all_inheritors(Oid parentrel) } /* - * find_inheritable_rt_entry - - * Given a rangetable, find the first rangetable entry that represents - * an inheritance set. + * expand_inherted_rtentry + * Check whether a rangetable entry represents an inheritance set. + * If so, add entries for all the child tables to the query's + * rangetable, and return an integer list of RT indexes for the + * whole inheritance set (parent and children). + * If not, return NIL. * - * If successful, set *rt_index to the index (1..n) of the entry, - * set *inheritors to a list of the relation OIDs of the set, - * and return TRUE. + * A childless table is never considered to be an inheritance set; therefore + * the result will never be a one-element list. It'll be either empty + * or have two or more elements. * - * If there is no entry that requires inheritance processing, - * return FALSE. - * - * NOTE: We return the inheritors list so that plan_inherit_queries doesn't - * have to compute it again. - * - * NOTE: We clear the inh flag in any entries that have it set but turn - * out not to have any actual inheritance children. This is an efficiency - * hack to avoid having to repeat the inheritance checks if the list is - * scanned again (as will happen during expansion of any subsequent entry - * that does have inheritance children). Although modifying the input - * rangetable in-place may seem uncool, there's no reason not to do it, - * since any re-examination of the entry would just come to the same - * conclusion that the table has no children. + * NOTE: after this routine executes, the specified RTE will always have + * its inh flag cleared, whether or not there were any children. This + * ensures we won't expand the same RTE twice, which would otherwise occur + * for the case of an inherited UPDATE/DELETE target relation. */ -bool -find_inheritable_rt_entry(List *rangetable, - Index *rt_index, - List **inheritors) +List * +expand_inherted_rtentry(Query *parse, Index rti) { - Index count = 0; - List *temp; + RangeTblEntry *rte = rt_fetch(rti, parse->rtable); + Oid parentOID = rte->relid; + List *inhOIDs; + List *inhRTIs; + List *l; - foreach(temp, rangetable) + /* Does RT entry allow inheritance? */ + if (! rte->inh) + return NIL; + Assert(parentOID != InvalidOid && rte->subquery == NULL); + /* Always clear the parent's inh flag, see above comments */ + rte->inh = false; + /* Fast path for common case of childless table */ + if (! has_subclass(parentOID)) + return NIL; + /* Scan for all members of inheritance set */ + inhOIDs = find_all_inheritors(parentOID); + /* + * Check that there's at least one descendant, else treat as + * no-child case. This could happen despite above has_subclass() + * check, if table once had a child but no longer does. + */ + if (lnext(inhOIDs) == NIL) + return NIL; + /* OK, it's an inheritance set; expand it */ + inhRTIs = makeListi1(rti); + foreach(l, inhOIDs) { - RangeTblEntry *rt_entry = (RangeTblEntry *) lfirst(temp); - List *inhs; + Oid childOID = (Oid) lfirsti(l); + RangeTblEntry *childrte; + Index childRTindex; - count++; - /* Ignore non-inheritable RT entries */ - if (! rt_entry->inh) + /* parent will be in the list too, so ignore it */ + if (childOID == parentOID) continue; - /* Fast path for common case of childless table */ - if (! has_subclass(rt_entry->relid)) - { - rt_entry->inh = false; - continue; - } - /* Scan for all members of inheritance set */ - inhs = find_all_inheritors(rt_entry->relid); + /* - * Check that there's at least one descendant, else treat as - * no-child case. This could happen despite above has_subclass() - * check, if table once had a child but no longer does. + * Build an RTE for the child, and attach to query's rangetable list. + * We copy most fields of the parent's RTE, but replace relation + * real name and OID. Note that inh will be false at this point. */ - if (lnext(inhs) == NIL) - { - rt_entry->inh = false; - continue; - } - /* OK, found our boy */ - *rt_index = count; - *inheritors = inhs; - return true; + childrte = copyObject(rte); + childrte->relname = get_rel_name(childOID); + childrte->relid = childOID; + parse->rtable = lappend(parse->rtable, childrte); + childRTindex = length(parse->rtable); + + inhRTIs = lappendi(inhRTIs, childRTindex); + } + return inhRTIs; +} + +/* + * adjust_inherited_attrs + * Copy the specified query or expression and translate Vars referring + * to old_rt_index to refer to new_rt_index. + * + * We also adjust varattno to match the new table by column name, rather + * than column number. This hack makes it possible for child tables to have + * different column positions for the "same" attribute as a parent, which + * helps ALTER TABLE ADD COLUMN. Unfortunately this isn't nearly enough to + * make it work transparently; there are other places where things fall down + * if children and parents don't have the same column numbers for inherited + * attributes. It'd be better to rip this code out and fix ALTER TABLE... + */ +Node * +adjust_inherited_attrs(Node *node, + Index old_rt_index, Oid old_relid, + Index new_rt_index, Oid new_relid) +{ + adjust_inherited_attrs_context context; + + /* Handle simple case simply... */ + if (old_rt_index == new_rt_index) + { + Assert(old_relid == new_relid); + return copyObject(node); } - return false; -} - -/* - * new_rangetable_entry - - * Replaces the name and relid of 'old_entry' with the values for - * 'new_relid'. - * - * Returns a copy of 'old_entry' with the parameters substituted. - */ -static RangeTblEntry * -new_rangetable_entry(Oid new_relid, RangeTblEntry *old_entry) -{ - RangeTblEntry *new_entry = copyObject(old_entry); - - /* Replace relation real name and OID, but not the reference name */ - new_entry->relname = get_rel_name(new_relid); - new_entry->relid = new_relid; - return new_entry; -} - -/* - * fix_parsetree_attnums - * Replaces attribute numbers from the relation represented by - * 'old_relid' in 'parsetree' with the attribute numbers from - * 'new_relid'. - * - * The parsetree is MODIFIED IN PLACE. This is OK only because - * plan_inherit_queries made a copy of the tree for us to hack upon. - */ -static void -fix_parsetree_attnums(Index rt_index, - Oid old_relid, - Oid new_relid, - Query *parsetree) -{ - fix_parsetree_attnums_context context; - - if (old_relid == new_relid) - return; /* no work needed for parent rel itself */ - - context.rt_index = rt_index; + context.old_rt_index = old_rt_index; + context.new_rt_index = new_rt_index; context.old_relid = old_relid; context.new_relid = new_relid; - context.sublevels_up = 0; - query_tree_walker(parsetree, - fix_parsetree_attnums_walker, - (void *) &context, - true); + /* + * Must be prepared to start with a Query or a bare expression tree. + */ + if (node && IsA(node, Query)) + { + Query *query = (Query *) node; + Query *newnode; + + FLATCOPY(newnode, query, Query); + if (newnode->resultRelation == old_rt_index) + newnode->resultRelation = new_rt_index; + query_tree_mutator(newnode, adjust_inherited_attrs_mutator, + (void *) &context, false); + return (Node *) newnode; + } + else + return adjust_inherited_attrs_mutator(node, &context); } -/* - * Adjust varnos for child tables. This routine makes it possible for - * child tables to have different column positions for the "same" attribute - * as a parent, which helps ALTER TABLE ADD COLUMN. Unfortunately this isn't - * nearly enough to make it work transparently; there are other places where - * things fall down if children and parents don't have the same column numbers - * for inherited attributes. It'd be better to rip this code out and fix - * ALTER TABLE... - */ -static bool -fix_parsetree_attnums_walker(Node *node, - fix_parsetree_attnums_context *context) +static Node * +adjust_inherited_attrs_mutator(Node *node, + adjust_inherited_attrs_context *context) { if (node == NULL) - return false; + return NULL; if (IsA(node, Var)) { - Var *var = (Var *) node; + Var *var = (Var *) copyObject(node); - if (var->varlevelsup == context->sublevels_up && - var->varno == context->rt_index && - var->varattno > 0) + if (var->varlevelsup == 0 && + var->varno == context->old_rt_index) { - var->varattno = get_attnum(context->new_relid, - get_attname(context->old_relid, - var->varattno)); + var->varno = context->new_rt_index; + if (var->varattno > 0) + var->varattno = get_attnum(context->new_relid, + get_attname(context->old_relid, + var->varattno)); } - return false; + return (Node *) var; } - if (IsA(node, Query)) + if (IsA(node, RangeTblRef)) { - /* Recurse into subselects */ - bool result; + RangeTblRef *rtr = (RangeTblRef *) copyObject(node); - context->sublevels_up++; - result = query_tree_walker((Query *) node, - fix_parsetree_attnums_walker, - (void *) context, - true); - context->sublevels_up--; - return result; + if (rtr->rtindex == context->old_rt_index) + rtr->rtindex = context->new_rt_index; + return (Node *) rtr; } - return expression_tree_walker(node, fix_parsetree_attnums_walker, + /* + * We have to process RestrictInfo nodes specially: we do NOT want to + * copy the original subclauseindices list, since the new rel may have + * different indices. The list will be rebuilt during planning anyway. + */ + if (IsA(node, RestrictInfo)) + { + RestrictInfo *oldinfo = (RestrictInfo *) node; + RestrictInfo *newinfo = makeNode(RestrictInfo); + + /* Copy all flat-copiable fields */ + memcpy(newinfo, oldinfo, sizeof(RestrictInfo)); + + newinfo->clause = (Expr *) + adjust_inherited_attrs_mutator((Node *) oldinfo->clause, context); + + newinfo->subclauseindices = NIL; + + return (Node *) newinfo; + } + /* + * NOTE: we do not need to recurse into sublinks, because they should + * already have been converted to subplans before we see them. + */ + return expression_tree_mutator(node, adjust_inherited_attrs_mutator, (void *) context); } - -static Append * -make_append(List *appendplans, - Index rt_index, - List *inheritrtable, - List *tlist) -{ - Append *node = makeNode(Append); - List *subnode; - - node->appendplans = appendplans; - node->inheritrelid = rt_index; - node->inheritrtable = inheritrtable; - node->plan.startup_cost = 0; - node->plan.total_cost = 0; - node->plan.plan_rows = 0; - node->plan.plan_width = 0; - foreach(subnode, appendplans) - { - Plan *subplan = (Plan *) lfirst(subnode); - - if (subnode == appendplans) /* first node? */ - node->plan.startup_cost = subplan->startup_cost; - node->plan.total_cost += subplan->total_cost; - node->plan.plan_rows += subplan->plan_rows; - if (node->plan.plan_width < subplan->plan_width) - node->plan.plan_width = subplan->plan_width; - } - node->plan.state = (EState *) NULL; - node->plan.targetlist = tlist; - node->plan.qual = NIL; - node->plan.lefttree = (Plan *) NULL; - node->plan.righttree = (Plan *) NULL; - - return node; -} diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c index 36843f834da..b51ec089347 100644 --- a/src/backend/optimizer/util/pathnode.c +++ b/src/backend/optimizer/util/pathnode.c @@ -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, diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c index 86258b0f644..64720944ce3 100644 --- a/src/backend/optimizer/util/relnode.c +++ b/src/backend/optimizer/util/relnode.c @@ -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; } diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c index 60521d13475..cbd19f0aae4 100644 --- a/src/backend/parser/parse_clause.c +++ b/src/backend/parser/parse_clause.c @@ -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. */ diff --git a/src/backend/tcop/pquery.c b/src/backend/tcop/pquery.c index 62848d77348..248baa3f894 100644 --- a/src/backend/tcop/pquery.c +++ b/src/backend/tcop/pquery.c @@ -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; diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index 3deef21855f..67a2beb77c3 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -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 $ * *------------------------------------------------------------------------- */ @@ -52,7 +52,7 @@ * catalog changes on the same day...) */ -/* yyyymmddN */ -#define CATALOG_VERSION_NO 200011110 +/* yyyymmddN */ +#define CATALOG_VERSION_NO 200011112 #endif diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h index 9fe59b031a7..2197af567fd 100644 --- a/src/include/executor/executor.h +++ b/src/include/executor/executor.h @@ -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); diff --git a/src/include/executor/tuptable.h b/src/include/executor/tuptable.h index a8edfae6740..8aa0ba39ecd 100644 --- a/src/include/executor/tuptable.h +++ b/src/include/executor/tuptable.h @@ -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; /* ---------------- diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index 14cd94baa07..4ba3a70c1b5 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -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; /* ---------------------------------------------------------------- diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h index 727db39d827..6b64961525a 100644 --- a/src/include/nodes/nodes.h +++ b/src/include/nodes/nodes.h @@ -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, diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 269fa9592be..469230e1c62 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -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 */ diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h index 177ab73a13b..eecb74caf2f 100644 --- a/src/include/nodes/plannodes.h +++ b/src/include/nodes/plannodes.h @@ -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; diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h index 1c864f2e721..45d53be5f62 100644 --- a/src/include/nodes/relation.h +++ b/src/include/nodes/relation.h @@ -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. */ diff --git a/src/include/optimizer/pathnode.h b/src/include/optimizer/pathnode.h index 06912f192f6..993003eaa2f 100644 --- a/src/include/optimizer/pathnode.h +++ b/src/include/optimizer/pathnode.h @@ -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); diff --git a/src/include/optimizer/planmain.h b/src/include/optimizer/planmain.h index 2d5de645046..f50c1113f06 100644 --- a/src/include/optimizer/planmain.h +++ b/src/include/optimizer/planmain.h @@ -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 diff --git a/src/include/optimizer/planner.h b/src/include/optimizer/planner.h index 3fc0f435dc3..9467b6485bb 100644 --- a/src/include/optimizer/planner.h +++ b/src/include/optimizer/planner.h @@ -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); diff --git a/src/include/optimizer/prep.h b/src/include/optimizer/prep.h index dcdf6fb62f6..be1811d5871 100644 --- a/src/include/optimizer/prep.h +++ b/src/include/optimizer/prep.h @@ -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 */