diff --git a/doc/src/sgml/indexam.sgml b/doc/src/sgml/indexam.sgml index d559be0b6eb5bada5f380a22228ff960edbfcbe3..247f7f48cbef4568fbcff47c6b4a0953d03ed527 100644 --- a/doc/src/sgml/indexam.sgml +++ b/doc/src/sgml/indexam.sgml @@ -1,4 +1,4 @@ -<!-- $PostgreSQL: pgsql/doc/src/sgml/indexam.sgml,v 2.21 2007/01/31 20:56:17 momjian Exp $ --> +<!-- $PostgreSQL: pgsql/doc/src/sgml/indexam.sgml,v 2.22 2007/02/22 22:00:22 tgl Exp $ --> <chapter id="indexam"> <title>Index Access Method Interface Definition</title> @@ -903,7 +903,7 @@ amcostestimate (PlannerInfo *root, * Also, we charge for evaluation of the indexquals at each index row. * All the costs are assumed to be paid incrementally during the scan. */ - cost_qual_eval(&index_qual_cost, indexQuals); + cost_qual_eval(&index_qual_cost, indexQuals, root); *indexStartupCost = index_qual_cost.startup; *indexTotalCost = seq_page_cost * numIndexPages + (cpu_index_tuple_cost + index_qual_cost.per_tuple) * numIndexTuples; diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c index 58b7e6ded9a48b1a74b344a45022fba02159e298..34784eb078e8d47306d0ea21247b729df52ca7bb 100644 --- a/src/backend/commands/explain.c +++ b/src/backend/commands/explain.c @@ -7,7 +7,7 @@ * Portions Copyright (c) 1994-5, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/explain.c,v 1.156 2007/02/20 17:32:14 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/commands/explain.c,v 1.157 2007/02/22 22:00:22 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -37,6 +37,7 @@ typedef struct ExplainState bool printNodes; /* do nodeToString() too */ bool printAnalyze; /* print actual times */ /* other states */ + PlannedStmt *pstmt; /* top of plan */ List *rtable; /* range table */ } ExplainState; @@ -260,6 +261,7 @@ ExplainOnePlan(QueryDesc *queryDesc, ExplainStmt *stmt, es->printNodes = stmt->verbose; es->printAnalyze = stmt->analyze; + es->pstmt = queryDesc->plannedstmt; es->rtable = queryDesc->plannedstmt->rtable; if (es->printNodes) @@ -858,7 +860,6 @@ explain_outNode(StringInfo str, /* initPlan-s */ if (plan->initPlan) { - List *saved_rtable = es->rtable; ListCell *lst; for (i = 0; i < indent; i++) @@ -869,16 +870,15 @@ explain_outNode(StringInfo str, SubPlanState *sps = (SubPlanState *) lfirst(lst); SubPlan *sp = (SubPlan *) sps->xprstate.expr; - es->rtable = sp->rtable; for (i = 0; i < indent; i++) appendStringInfo(str, " "); appendStringInfo(str, " -> "); - explain_outNode(str, sp->plan, + explain_outNode(str, + exec_subplan_get_plan(es->pstmt, sp), sps->planstate, NULL, indent + 4, es); } - es->rtable = saved_rtable; } /* lefttree */ @@ -994,12 +994,6 @@ explain_outNode(StringInfo str, SubqueryScan *subqueryscan = (SubqueryScan *) plan; SubqueryScanState *subquerystate = (SubqueryScanState *) planstate; Plan *subnode = subqueryscan->subplan; - RangeTblEntry *rte = rt_fetch(subqueryscan->scan.scanrelid, - es->rtable); - List *saved_rtable = es->rtable; - - Assert(rte->rtekind == RTE_SUBQUERY); - es->rtable = rte->subquery->rtable; for (i = 0; i < indent; i++) appendStringInfo(str, " "); @@ -1009,14 +1003,11 @@ explain_outNode(StringInfo str, subquerystate->subplan, NULL, indent + 3, es); - - es->rtable = saved_rtable; } /* subPlan-s */ if (planstate->subPlan) { - List *saved_rtable = es->rtable; ListCell *lst; for (i = 0; i < indent; i++) @@ -1027,16 +1018,15 @@ explain_outNode(StringInfo str, SubPlanState *sps = (SubPlanState *) lfirst(lst); SubPlan *sp = (SubPlan *) sps->xprstate.expr; - es->rtable = sp->rtable; for (i = 0; i < indent; i++) appendStringInfo(str, " "); appendStringInfo(str, " -> "); - explain_outNode(str, sp->plan, + explain_outNode(str, + exec_subplan_get_plan(es->pstmt, sp), sps->planstate, NULL, indent + 4, es); } - es->rtable = saved_rtable; } } diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index 405b58f9fd6b087ec18ecda7541f7b1a98e63c9c..d0df0ea6f472f28e48f822234b6cab7bfa85ba8c 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -26,7 +26,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/executor/execMain.c,v 1.287 2007/02/20 17:32:14 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/executor/execMain.c,v 1.288 2007/02/22 22:00:22 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -92,9 +92,9 @@ static void ExecProcessReturning(ProjectionInfo *projectReturning, DestReceiver *dest); static TupleTableSlot *EvalPlanQualNext(EState *estate); static void EndEvalPlanQual(EState *estate); +static void ExecCheckRTPerms(List *rangeTable); static void ExecCheckRTEPerms(RangeTblEntry *rte); static void ExecCheckXactReadOnly(PlannedStmt *plannedstmt); -static void ExecCheckRangeTblReadOnly(List *rtable); static void EvalPlanQualStart(evalPlanQual *epq, EState *estate, evalPlanQual *priorepq); static void EvalPlanQualStop(evalPlanQual *epq); @@ -348,16 +348,14 @@ ExecutorRewind(QueryDesc *queryDesc) * ExecCheckRTPerms * Check access permissions for all relations listed in a range table. */ -void +static void ExecCheckRTPerms(List *rangeTable) { ListCell *l; foreach(l, rangeTable) { - RangeTblEntry *rte = lfirst(l); - - ExecCheckRTEPerms(rte); + ExecCheckRTEPerms((RangeTblEntry *) lfirst(l)); } } @@ -373,12 +371,9 @@ ExecCheckRTEPerms(RangeTblEntry *rte) Oid userid; /* - * Only plain-relation RTEs need to be checked here. Subquery RTEs are - * checked by ExecInitSubqueryScan if the subquery is still a separate - * subquery --- if it's been pulled up into our query level then the RTEs - * are in our rangetable and will be checked here. Function RTEs are + * Only plain-relation RTEs need to be checked here. Function RTEs are * checked by init_fcache when the function is prepared for execution. - * Join and special RTEs need no checks. + * Join, subquery, and special RTEs need no checks. */ if (rte->rtekind != RTE_RELATION) return; @@ -417,6 +412,8 @@ ExecCheckRTEPerms(RangeTblEntry *rte) static void ExecCheckXactReadOnly(PlannedStmt *plannedstmt) { + ListCell *l; + /* * CREATE TABLE AS or SELECT INTO? * @@ -426,32 +423,9 @@ ExecCheckXactReadOnly(PlannedStmt *plannedstmt) goto fail; /* Fail if write permissions are requested on any non-temp table */ - ExecCheckRangeTblReadOnly(plannedstmt->rtable); - - return; - -fail: - ereport(ERROR, - (errcode(ERRCODE_READ_ONLY_SQL_TRANSACTION), - errmsg("transaction is read-only"))); -} - -static void -ExecCheckRangeTblReadOnly(List *rtable) -{ - ListCell *l; - - /* Fail if write permissions are requested on any non-temp table */ - foreach(l, rtable) + foreach(l, plannedstmt->rtable) { - RangeTblEntry *rte = lfirst(l); - - if (rte->rtekind == RTE_SUBQUERY) - { - Assert(!rte->subquery->into); - ExecCheckRangeTblReadOnly(rte->subquery->rtable); - continue; - } + RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); if (rte->rtekind != RTE_RELATION) continue; @@ -494,9 +468,7 @@ InitPlan(QueryDesc *queryDesc, int eflags) ListCell *l; /* - * Do permissions checks. It's sufficient to examine the query's top - * rangetable here --- subplan RTEs will be checked during - * ExecInitSubPlan(). + * Do permissions checks */ ExecCheckRTPerms(rangeTable); diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c index d9957573883b3bf909c44f2a5ef775feec3f5b77..d188a38489d1e470202e306d2a9fc1dc82f7e841 100644 --- a/src/backend/executor/execUtils.c +++ b/src/backend/executor/execUtils.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/executor/execUtils.c,v 1.145 2007/02/20 17:32:14 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/executor/execUtils.c,v 1.146 2007/02/22 22:00:22 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -847,7 +847,6 @@ ExecRelationIsTargetRelation(EState *estate, Index scanrelid) Relation ExecOpenScanRelation(EState *estate, Index scanrelid) { - RangeTblEntry *rtentry; Oid reloid; LOCKMODE lockmode; ResultRelInfo *resultRelInfos; @@ -885,8 +884,7 @@ ExecOpenScanRelation(EState *estate, Index scanrelid) } /* OK, open the relation and acquire lock as needed */ - rtentry = rt_fetch(scanrelid, estate->es_range_table); - reloid = rtentry->relid; + reloid = getrelid(scanrelid, estate->es_range_table); return heap_open(reloid, lockmode); } diff --git a/src/backend/executor/nodeFunctionscan.c b/src/backend/executor/nodeFunctionscan.c index d3d9886e3c3aee128500537974eec19cf7893b27..87ac754a1e37e556282a78ed1edcf469bf05f52b 100644 --- a/src/backend/executor/nodeFunctionscan.c +++ b/src/backend/executor/nodeFunctionscan.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/executor/nodeFunctionscan.c,v 1.43 2007/02/19 02:23:11 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/executor/nodeFunctionscan.c,v 1.44 2007/02/22 22:00:23 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -24,7 +24,6 @@ #include "executor/nodeFunctionscan.h" #include "funcapi.h" -#include "parser/parsetree.h" #include "utils/builtins.h" diff --git a/src/backend/executor/nodeSubplan.c b/src/backend/executor/nodeSubplan.c index 32167a94efbff491b18797034acac2422c90ad5d..9bc96921f416fcd77736707e71f2142cbb88f779 100644 --- a/src/backend/executor/nodeSubplan.c +++ b/src/backend/executor/nodeSubplan.c @@ -7,7 +7,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/executor/nodeSubplan.c,v 1.85 2007/02/06 02:59:11 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/executor/nodeSubplan.c,v 1.86 2007/02/22 22:00:23 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -637,13 +637,9 @@ void ExecInitSubPlan(SubPlanState *node, EState *estate, int eflags) { SubPlan *subplan = (SubPlan *) node->xprstate.expr; + Plan *plan = exec_subplan_get_plan(estate->es_plannedstmt, subplan); EState *sp_estate; - /* - * Do access checking on the rangetable entries in the subquery. - */ - ExecCheckRTPerms(subplan->rtable); - /* * initialize my state */ @@ -668,18 +664,21 @@ ExecInitSubPlan(SubPlanState *node, EState *estate, int eflags) * shares our Param ID space and es_query_cxt, however. XXX if rangetable * access were done differently, the subquery could share our EState, * which would eliminate some thrashing about in this module... + * + * XXX make that happen! */ sp_estate = CreateSubExecutorState(estate); node->sub_estate = sp_estate; - sp_estate->es_range_table = subplan->rtable; + sp_estate->es_range_table = estate->es_range_table; sp_estate->es_param_list_info = estate->es_param_list_info; sp_estate->es_param_exec_vals = estate->es_param_exec_vals; sp_estate->es_tupleTable = - ExecCreateTupleTable(ExecCountSlotsNode(subplan->plan) + 10); + ExecCreateTupleTable(ExecCountSlotsNode(plan) + 10); sp_estate->es_snapshot = estate->es_snapshot; sp_estate->es_crosscheck_snapshot = estate->es_crosscheck_snapshot; sp_estate->es_instrument = estate->es_instrument; + sp_estate->es_plannedstmt = estate->es_plannedstmt; /* * Start up the subplan (this is a very cut-down form of InitPlan()) @@ -692,7 +691,7 @@ ExecInitSubPlan(SubPlanState *node, EState *estate, int eflags) if (subplan->parParam == NIL && subplan->setParam == NIL) eflags |= EXEC_FLAG_REWIND; - node->planstate = ExecInitNode(subplan->plan, sp_estate, eflags); + node->planstate = ExecInitNode(plan, sp_estate, eflags); node->needShutdown = true; /* now we need to shutdown the subplan */ diff --git a/src/backend/executor/nodeSubqueryscan.c b/src/backend/executor/nodeSubqueryscan.c index 24d470d8edd1f914698ecc312eb3705d6c7ae86c..6d58a8cad4e63b92b273dde8f6fabb934a473099 100644 --- a/src/backend/executor/nodeSubqueryscan.c +++ b/src/backend/executor/nodeSubqueryscan.c @@ -12,7 +12,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/executor/nodeSubqueryscan.c,v 1.35 2007/01/05 22:19:28 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/executor/nodeSubqueryscan.c,v 1.36 2007/02/22 22:00:23 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -29,7 +29,6 @@ #include "executor/execdebug.h" #include "executor/nodeSubqueryscan.h" -#include "parser/parsetree.h" static TupleTableSlot *SubqueryNext(SubqueryScanState *node); @@ -104,17 +103,18 @@ SubqueryScanState * ExecInitSubqueryScan(SubqueryScan *node, EState *estate, int eflags) { SubqueryScanState *subquerystate; - RangeTblEntry *rte; EState *sp_estate; /* check for unsupported flags */ Assert(!(eflags & EXEC_FLAG_MARK)); /* - * SubqueryScan should not have any "normal" children. + * SubqueryScan should not have any "normal" children. Also, if planner + * left anything in subrtable, it's fishy. */ Assert(outerPlan(node) == NULL); Assert(innerPlan(node) == NULL); + Assert(node->subrtable == NIL); /* * create state structure @@ -152,25 +152,18 @@ ExecInitSubqueryScan(SubqueryScan *node, EState *estate, int eflags) * initialize subquery * * This should agree with ExecInitSubPlan - */ - rte = rt_fetch(node->scan.scanrelid, estate->es_range_table); - Assert(rte->rtekind == RTE_SUBQUERY); - - /* - * Do access checking on the rangetable entries in the subquery. - */ - ExecCheckRTPerms(rte->subquery->rtable); - - /* + * * The subquery needs its own EState because it has its own rangetable. It * shares our Param ID space and es_query_cxt, however. XXX if rangetable * access were done differently, the subquery could share our EState, * which would eliminate some thrashing about in this module... + * + * XXX make that happen! */ sp_estate = CreateSubExecutorState(estate); subquerystate->sss_SubEState = sp_estate; - sp_estate->es_range_table = rte->subquery->rtable; + sp_estate->es_range_table = estate->es_range_table; sp_estate->es_param_list_info = estate->es_param_list_info; sp_estate->es_param_exec_vals = estate->es_param_exec_vals; sp_estate->es_tupleTable = @@ -178,6 +171,7 @@ ExecInitSubqueryScan(SubqueryScan *node, EState *estate, int eflags) sp_estate->es_snapshot = estate->es_snapshot; sp_estate->es_crosscheck_snapshot = estate->es_crosscheck_snapshot; sp_estate->es_instrument = estate->es_instrument; + sp_estate->es_plannedstmt = estate->es_plannedstmt; /* * Start up the subplan (this is a very cut-down form of InitPlan()) diff --git a/src/backend/executor/nodeValuesscan.c b/src/backend/executor/nodeValuesscan.c index 96e4b98a4e25cd947c30d2b5f16ca85796264810..bfd837e0985900937723c7b6abbbeaa707cfb3c2 100644 --- a/src/backend/executor/nodeValuesscan.c +++ b/src/backend/executor/nodeValuesscan.c @@ -9,7 +9,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/executor/nodeValuesscan.c,v 1.6 2007/02/19 02:23:11 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/executor/nodeValuesscan.c,v 1.7 2007/02/22 22:00:23 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -25,7 +25,6 @@ #include "executor/executor.h" #include "executor/nodeValuesscan.h" -#include "parser/parsetree.h" #include "utils/memutils.h" diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index 4d10afc022db95447f238c1fc51894d048fb9bf0..efa1f86945337a9857df0cb0b6fc8f5b6d4d88ba 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 - * $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.367 2007/02/20 17:32:15 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.368 2007/02/22 22:00:23 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -78,6 +78,7 @@ _copyPlannedStmt(PlannedStmt *from) COPY_NODE_FIELD(rtable); COPY_NODE_FIELD(resultRelations); COPY_NODE_FIELD(into); + COPY_NODE_FIELD(subplans); COPY_NODE_FIELD(returningLists); COPY_NODE_FIELD(rowMarks); COPY_SCALAR_FIELD(nParamExec); @@ -366,6 +367,7 @@ _copySubqueryScan(SubqueryScan *from) * copy remainder of node */ COPY_NODE_FIELD(subplan); + COPY_NODE_FIELD(subrtable); return newnode; } @@ -957,9 +959,8 @@ _copySubPlan(SubPlan *from) COPY_SCALAR_FIELD(subLinkType); COPY_NODE_FIELD(testexpr); COPY_NODE_FIELD(paramIds); - COPY_NODE_FIELD(plan); COPY_SCALAR_FIELD(plan_id); - COPY_NODE_FIELD(rtable); + COPY_SCALAR_FIELD(firstColType); COPY_SCALAR_FIELD(useHashTable); COPY_SCALAR_FIELD(unknownEqFalse); COPY_NODE_FIELD(setParam); diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index dafa15f0287dcd849e36d1a8e3222a1de9acd5b6..1007930814ac36694122856b47582bb99621fd0e 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -18,7 +18,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.299 2007/02/20 17:32:15 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.300 2007/02/22 22:00:23 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -306,9 +306,8 @@ _equalSubPlan(SubPlan *a, SubPlan *b) COMPARE_SCALAR_FIELD(subLinkType); COMPARE_NODE_FIELD(testexpr); COMPARE_NODE_FIELD(paramIds); - /* should compare plans, but have to settle for comparing plan IDs */ COMPARE_SCALAR_FIELD(plan_id); - COMPARE_NODE_FIELD(rtable); + COMPARE_SCALAR_FIELD(firstColType); COMPARE_SCALAR_FIELD(useHashTable); COMPARE_SCALAR_FIELD(unknownEqFalse); COMPARE_NODE_FIELD(setParam); diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index c8cc25abb1330ce9c8cc25d8dd90375bafbf0f6c..64f235201d0def2ba1d63eeae6d7b5b029d729b8 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.300 2007/02/20 17:32:15 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.301 2007/02/22 22:00:23 tgl Exp $ * * NOTES * Every node type that can appear in stored rules' parsetrees *must* @@ -245,6 +245,7 @@ _outPlannedStmt(StringInfo str, PlannedStmt *node) WRITE_NODE_FIELD(rtable); WRITE_NODE_FIELD(resultRelations); WRITE_NODE_FIELD(into); + WRITE_NODE_FIELD(subplans); WRITE_NODE_FIELD(returningLists); WRITE_NODE_FIELD(rowMarks); WRITE_INT_FIELD(nParamExec); @@ -415,6 +416,7 @@ _outSubqueryScan(StringInfo str, SubqueryScan *node) _outScanInfo(str, (Scan *) node); WRITE_NODE_FIELD(subplan); + WRITE_NODE_FIELD(subrtable); } static void @@ -823,9 +825,8 @@ _outSubPlan(StringInfo str, SubPlan *node) WRITE_ENUM_FIELD(subLinkType, SubLinkType); WRITE_NODE_FIELD(testexpr); WRITE_NODE_FIELD(paramIds); - WRITE_NODE_FIELD(plan); WRITE_INT_FIELD(plan_id); - WRITE_NODE_FIELD(rtable); + WRITE_OID_FIELD(firstColType); WRITE_BOOL_FIELD(useHashTable); WRITE_BOOL_FIELD(unknownEqFalse); WRITE_NODE_FIELD(setParam); @@ -1259,7 +1260,9 @@ _outPlannerGlobal(StringInfo str, PlannerGlobal *node) /* NB: this isn't a complete set of fields */ WRITE_NODE_FIELD(paramlist); - WRITE_INT_FIELD(next_plan_id); + WRITE_NODE_FIELD(subplans); + WRITE_NODE_FIELD(subrtables); + WRITE_NODE_FIELD(finalrtable); } static void @@ -1317,6 +1320,7 @@ _outRelOptInfo(StringInfo str, RelOptInfo *node) WRITE_UINT_FIELD(pages); WRITE_FLOAT_FIELD(tuples, "%.0f"); WRITE_NODE_FIELD(subplan); + WRITE_NODE_FIELD(subrtable); WRITE_NODE_FIELD(baserestrictinfo); WRITE_NODE_FIELD(joininfo); WRITE_BOOL_FIELD(has_eclass_joins); diff --git a/src/backend/nodes/print.c b/src/backend/nodes/print.c index 06d28b56b21641c959e113c50ede6915dba6594d..c6edfbed8a054d465d55b01c8d35eb4e54a72127 100644 --- a/src/backend/nodes/print.c +++ b/src/backend/nodes/print.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/nodes/print.c,v 1.84 2007/02/10 14:58:54 petere Exp $ + * $PostgreSQL: pgsql/src/backend/nodes/print.c,v 1.85 2007/02/22 22:00:23 tgl Exp $ * * HISTORY * AUTHOR DATE MAJOR EVENT @@ -25,7 +25,6 @@ #include "parser/parsetree.h" #include "utils/lsyscache.h" -static char *plannode_type(Plan *p); /* * print @@ -488,171 +487,3 @@ print_slot(TupleTableSlot *slot) debugtup(slot, NULL); } - -static char * -plannode_type(Plan *p) -{ - switch (nodeTag(p)) - { - case T_Plan: - return "PLAN"; - case T_Result: - return "RESULT"; - case T_Append: - return "APPEND"; - case T_BitmapAnd: - return "BITMAPAND"; - case T_BitmapOr: - return "BITMAPOR"; - case T_Scan: - return "SCAN"; - case T_SeqScan: - return "SEQSCAN"; - case T_IndexScan: - return "INDEXSCAN"; - case T_BitmapIndexScan: - return "BITMAPINDEXSCAN"; - case T_BitmapHeapScan: - return "BITMAPHEAPSCAN"; - case T_TidScan: - return "TIDSCAN"; - case T_SubqueryScan: - return "SUBQUERYSCAN"; - case T_FunctionScan: - return "FUNCTIONSCAN"; - case T_ValuesScan: - return "VALUESSCAN"; - case T_Join: - return "JOIN"; - case T_NestLoop: - return "NESTLOOP"; - case T_MergeJoin: - return "MERGEJOIN"; - case T_HashJoin: - return "HASHJOIN"; - case T_Material: - return "MATERIAL"; - case T_Sort: - return "SORT"; - case T_Agg: - return "AGG"; - case T_Unique: - return "UNIQUE"; - case T_SetOp: - return "SETOP"; - case T_Limit: - return "LIMIT"; - case T_Hash: - return "HASH"; - case T_Group: - return "GROUP"; - default: - return "UNKNOWN"; - } -} - -/* - * Recursively prints a simple text description of the plan tree - */ -void -print_plan_recursive(Plan *p, Query *parsetree, int indentLevel, char *label) -{ - int i; - char extraInfo[NAMEDATALEN + 100]; - - if (!p) - return; - for (i = 0; i < indentLevel; i++) - printf(" "); - printf("%s%s :c=%.2f..%.2f :r=%.0f :w=%d ", label, plannode_type(p), - p->startup_cost, p->total_cost, - p->plan_rows, p->plan_width); - if (IsA(p, Scan) || - IsA(p, SeqScan) || - IsA(p, BitmapHeapScan)) - { - RangeTblEntry *rte; - - rte = rt_fetch(((Scan *) p)->scanrelid, parsetree->rtable); - strlcpy(extraInfo, rte->eref->aliasname, NAMEDATALEN); - } - else if (IsA(p, IndexScan)) - { - RangeTblEntry *rte; - - rte = rt_fetch(((IndexScan *) p)->scan.scanrelid, parsetree->rtable); - strlcpy(extraInfo, rte->eref->aliasname, NAMEDATALEN); - } - else if (IsA(p, FunctionScan)) - { - RangeTblEntry *rte; - - rte = rt_fetch(((FunctionScan *) p)->scan.scanrelid, parsetree->rtable); - strlcpy(extraInfo, rte->eref->aliasname, NAMEDATALEN); - } - else if (IsA(p, ValuesScan)) - { - RangeTblEntry *rte; - - rte = rt_fetch(((ValuesScan *) p)->scan.scanrelid, parsetree->rtable); - strlcpy(extraInfo, rte->eref->aliasname, NAMEDATALEN); - } - else - extraInfo[0] = '\0'; - if (extraInfo[0] != '\0') - printf(" ( %s )\n", extraInfo); - else - printf("\n"); - print_plan_recursive(p->lefttree, parsetree, indentLevel + 3, "l: "); - print_plan_recursive(p->righttree, parsetree, indentLevel + 3, "r: "); - - if (IsA(p, Append)) - { - ListCell *l; - Append *appendplan = (Append *) p; - - foreach(l, appendplan->appendplans) - { - Plan *subnode = (Plan *) lfirst(l); - - print_plan_recursive(subnode, parsetree, indentLevel + 3, "a: "); - } - } - - if (IsA(p, BitmapAnd)) - { - ListCell *l; - BitmapAnd *bitmapandplan = (BitmapAnd *) p; - - foreach(l, bitmapandplan->bitmapplans) - { - Plan *subnode = (Plan *) lfirst(l); - - print_plan_recursive(subnode, parsetree, indentLevel + 3, "a: "); - } - } - - if (IsA(p, BitmapOr)) - { - ListCell *l; - BitmapOr *bitmaporplan = (BitmapOr *) p; - - foreach(l, bitmaporplan->bitmapplans) - { - Plan *subnode = (Plan *) lfirst(l); - - print_plan_recursive(subnode, parsetree, indentLevel + 3, "a: "); - } - } -} - -/* - * print_plan - * - * prints just the plan node types - */ -void -print_plan(Plan *p, Query *parsetree) -{ - print_plan_recursive(p, parsetree, 0, ""); -} diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c index 6c6e80071c1ccfc12bbeed1a9d0c92373a750bb4..7aa2bd7e6fb2c050aefce6853fe93da4ea5ddee9 100644 --- a/src/backend/optimizer/path/allpaths.c +++ b/src/backend/optimizer/path/allpaths.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/optimizer/path/allpaths.c,v 1.160 2007/02/20 17:32:15 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/path/allpaths.c,v 1.161 2007/02/22 22:00:23 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -521,6 +521,7 @@ set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel, root->query_level + 1, tuple_fraction, &subroot); + rel->subrtable = subroot->parse->rtable; /* Copy number of output rows from subplan */ rel->tuples = rel->subplan->plan_rows; diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c index 422ef9232226ad82dd22d73f2d712b5eb80a0c90..3dbb3bd802dbd8cd7cef7b14d27c8eb62f97a604 100644 --- a/src/backend/optimizer/path/costsize.c +++ b/src/backend/optimizer/path/costsize.c @@ -54,7 +54,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/optimizer/path/costsize.c,v 1.177 2007/01/22 20:00:39 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/path/costsize.c,v 1.178 2007/02/22 22:00:24 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -107,11 +107,16 @@ bool enable_nestloop = true; bool enable_mergejoin = true; bool enable_hashjoin = true; +typedef struct +{ + PlannerInfo *root; + QualCost total; +} cost_qual_eval_context; static MergeScanSelCache *cached_scansel(PlannerInfo *root, RestrictInfo *rinfo, PathKey *pathkey); -static bool cost_qual_eval_walker(Node *node, QualCost *total); +static bool cost_qual_eval_walker(Node *node, cost_qual_eval_context *context); static Selectivity approx_selectivity(PlannerInfo *root, List *quals, JoinType jointype); static Selectivity join_in_selectivity(JoinPath *path, PlannerInfo *root); @@ -362,7 +367,7 @@ cost_index(IndexPath *path, PlannerInfo *root, { QualCost index_qual_cost; - cost_qual_eval(&index_qual_cost, indexQuals); + cost_qual_eval(&index_qual_cost, indexQuals, root); /* any startup cost still has to be paid ... */ cpu_per_tuple -= index_qual_cost.per_tuple; } @@ -855,7 +860,7 @@ cost_functionscan(Path *path, PlannerInfo *root, RelOptInfo *baserel) Assert(rte->rtekind == RTE_FUNCTION); /* Estimate costs of executing the function expression */ - cost_qual_eval_node(&exprcost, rte->funcexpr); + cost_qual_eval_node(&exprcost, rte->funcexpr, root); startup_cost += exprcost.startup; cpu_per_tuple = exprcost.per_tuple; @@ -1241,7 +1246,7 @@ cost_nestloop(NestPath *path, PlannerInfo *root) ntuples = outer_path_rows * inner_path_rows * joininfactor; /* CPU costs */ - cost_qual_eval(&restrict_qual_cost, path->joinrestrictinfo); + cost_qual_eval(&restrict_qual_cost, path->joinrestrictinfo, root); startup_cost += restrict_qual_cost.startup; cpu_per_tuple = cpu_tuple_cost + restrict_qual_cost.per_tuple; run_cost += cpu_per_tuple * ntuples; @@ -1301,8 +1306,8 @@ cost_mergejoin(MergePath *path, PlannerInfo *root) */ merge_selec = approx_selectivity(root, mergeclauses, path->jpath.jointype); - cost_qual_eval(&merge_qual_cost, mergeclauses); - cost_qual_eval(&qp_qual_cost, path->jpath.joinrestrictinfo); + cost_qual_eval(&merge_qual_cost, mergeclauses, root); + cost_qual_eval(&qp_qual_cost, path->jpath.joinrestrictinfo, root); qp_qual_cost.startup -= merge_qual_cost.startup; qp_qual_cost.per_tuple -= merge_qual_cost.per_tuple; @@ -1587,8 +1592,8 @@ cost_hashjoin(HashPath *path, PlannerInfo *root) */ hash_selec = approx_selectivity(root, hashclauses, path->jpath.jointype); - cost_qual_eval(&hash_qual_cost, hashclauses); - cost_qual_eval(&qp_qual_cost, path->jpath.joinrestrictinfo); + cost_qual_eval(&hash_qual_cost, hashclauses, root); + cost_qual_eval(&qp_qual_cost, path->jpath.joinrestrictinfo, root); qp_qual_cost.startup -= hash_qual_cost.startup; qp_qual_cost.per_tuple -= hash_qual_cost.per_tuple; @@ -1756,12 +1761,14 @@ cost_hashjoin(HashPath *path, PlannerInfo *root) * and a per-evaluation component. */ void -cost_qual_eval(QualCost *cost, List *quals) +cost_qual_eval(QualCost *cost, List *quals, PlannerInfo *root) { + cost_qual_eval_context context; ListCell *l; - cost->startup = 0; - cost->per_tuple = 0; + context.root = root; + context.total.startup = 0; + context.total.per_tuple = 0; /* We don't charge any cost for the implicit ANDing at top level ... */ @@ -1769,8 +1776,10 @@ cost_qual_eval(QualCost *cost, List *quals) { Node *qual = (Node *) lfirst(l); - cost_qual_eval_walker(qual, cost); + cost_qual_eval_walker(qual, &context); } + + *cost = context.total; } /* @@ -1778,15 +1787,21 @@ cost_qual_eval(QualCost *cost, List *quals) * As above, for a single RestrictInfo or expression. */ void -cost_qual_eval_node(QualCost *cost, Node *qual) +cost_qual_eval_node(QualCost *cost, Node *qual, PlannerInfo *root) { - cost->startup = 0; - cost->per_tuple = 0; - cost_qual_eval_walker(qual, cost); + cost_qual_eval_context context; + + context.root = root; + context.total.startup = 0; + context.total.per_tuple = 0; + + cost_qual_eval_walker(qual, &context); + + *cost = context.total; } static bool -cost_qual_eval_walker(Node *node, QualCost *total) +cost_qual_eval_walker(Node *node, cost_qual_eval_context *context) { if (node == NULL) return false; @@ -1803,18 +1818,19 @@ cost_qual_eval_walker(Node *node, QualCost *total) if (rinfo->eval_cost.startup < 0) { - rinfo->eval_cost.startup = 0; - rinfo->eval_cost.per_tuple = 0; + cost_qual_eval_context locContext; + + locContext.root = context->root; + locContext.total.startup = 0; + locContext.total.per_tuple = 0; /* * For an OR clause, recurse into the marked-up tree so that * we set the eval_cost for contained RestrictInfos too. */ if (rinfo->orclause) - cost_qual_eval_walker((Node *) rinfo->orclause, - &rinfo->eval_cost); + cost_qual_eval_walker((Node *) rinfo->orclause, &locContext); else - cost_qual_eval_walker((Node *) rinfo->clause, - &rinfo->eval_cost); + cost_qual_eval_walker((Node *) rinfo->clause, &locContext); /* * If the RestrictInfo is marked pseudoconstant, it will be tested * only once, so treat its cost as all startup cost. @@ -1822,12 +1838,13 @@ cost_qual_eval_walker(Node *node, QualCost *total) if (rinfo->pseudoconstant) { /* count one execution during startup */ - rinfo->eval_cost.startup += rinfo->eval_cost.per_tuple; - rinfo->eval_cost.per_tuple = 0; + locContext.total.startup += locContext.total.per_tuple; + locContext.total.per_tuple = 0; } + rinfo->eval_cost = locContext.total; } - total->startup += rinfo->eval_cost.startup; - total->per_tuple += rinfo->eval_cost.per_tuple; + context->total.startup += rinfo->eval_cost.startup; + context->total.per_tuple += rinfo->eval_cost.per_tuple; /* do NOT recurse into children */ return false; } @@ -1849,8 +1866,8 @@ cost_qual_eval_walker(Node *node, QualCost *total) */ if (IsA(node, FuncExpr)) { - total->per_tuple += get_func_cost(((FuncExpr *) node)->funcid) * - cpu_operator_cost; + context->total.per_tuple += + get_func_cost(((FuncExpr *) node)->funcid) * cpu_operator_cost; } else if (IsA(node, OpExpr) || IsA(node, DistinctExpr) || @@ -1858,8 +1875,8 @@ cost_qual_eval_walker(Node *node, QualCost *total) { /* rely on struct equivalence to treat these all alike */ set_opfuncid((OpExpr *) node); - total->per_tuple += get_func_cost(((OpExpr *) node)->opfuncid) * - cpu_operator_cost; + context->total.per_tuple += + get_func_cost(((OpExpr *) node)->opfuncid) * cpu_operator_cost; } else if (IsA(node, ScalarArrayOpExpr)) { @@ -1871,7 +1888,7 @@ cost_qual_eval_walker(Node *node, QualCost *total) Node *arraynode = (Node *) lsecond(saop->args); set_sa_opfuncid(saop); - total->per_tuple += get_func_cost(saop->opfuncid) * + context->total.per_tuple += get_func_cost(saop->opfuncid) * cpu_operator_cost * estimate_array_length(arraynode) * 0.5; } else if (IsA(node, RowCompareExpr)) @@ -1884,7 +1901,7 @@ cost_qual_eval_walker(Node *node, QualCost *total) { Oid opid = lfirst_oid(lc); - total->per_tuple += get_func_cost(get_opcode(opid)) * + context->total.per_tuple += get_func_cost(get_opcode(opid)) * cpu_operator_cost; } } @@ -1905,7 +1922,7 @@ cost_qual_eval_walker(Node *node, QualCost *total) * subplan by hashing. */ SubPlan *subplan = (SubPlan *) node; - Plan *plan = subplan->plan; + Plan *plan = planner_subplan_get_plan(context->root, subplan); if (subplan->useHashTable) { @@ -1915,7 +1932,7 @@ cost_qual_eval_walker(Node *node, QualCost *total) * cpu_operator_cost per tuple for the work of loading the * hashtable, too. */ - total->startup += plan->total_cost + + context->total.startup += plan->total_cost + cpu_operator_cost * plan->plan_rows; /* @@ -1941,20 +1958,21 @@ cost_qual_eval_walker(Node *node, QualCost *total) if (subplan->subLinkType == EXISTS_SUBLINK) { /* we only need to fetch 1 tuple */ - total->per_tuple += plan_run_cost / plan->plan_rows; + context->total.per_tuple += plan_run_cost / plan->plan_rows; } else if (subplan->subLinkType == ALL_SUBLINK || subplan->subLinkType == ANY_SUBLINK) { /* assume we need 50% of the tuples */ - total->per_tuple += 0.50 * plan_run_cost; + context->total.per_tuple += 0.50 * plan_run_cost; /* also charge a cpu_operator_cost per row examined */ - total->per_tuple += 0.50 * plan->plan_rows * cpu_operator_cost; + context->total.per_tuple += + 0.50 * plan->plan_rows * cpu_operator_cost; } else { /* assume we need all tuples */ - total->per_tuple += plan_run_cost; + context->total.per_tuple += plan_run_cost; } /* @@ -1967,15 +1985,15 @@ cost_qual_eval_walker(Node *node, QualCost *total) if (subplan->parParam == NIL && (IsA(plan, Sort) || IsA(plan, Material))) - total->startup += plan->startup_cost; + context->total.startup += plan->startup_cost; else - total->per_tuple += plan->startup_cost; + context->total.per_tuple += plan->startup_cost; } } /* recurse into children */ return expression_tree_walker(node, cost_qual_eval_walker, - (void *) total); + (void *) context); } @@ -2042,7 +2060,7 @@ set_baserel_size_estimates(PlannerInfo *root, RelOptInfo *rel) rel->rows = clamp_row_est(nrows); - cost_qual_eval(&rel->baserestrictcost, rel->baserestrictinfo); + cost_qual_eval(&rel->baserestrictcost, rel->baserestrictinfo, root); set_rel_width(root, rel); } diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c index 4d3d926a167fa58b1d2f06af0ae60013cbba26a3..dbfa2c4e58e415981f8b538a7ef535580dea9697 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -10,7 +10,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/optimizer/plan/createplan.c,v 1.225 2007/02/19 02:23:12 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/plan/createplan.c,v 1.226 2007/02/22 22:00:24 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -128,12 +128,12 @@ static Sort *make_sort(PlannerInfo *root, Plan *lefttree, int numCols, * create_plan * Creates the access plan for a query by tracing backwards through the * desired chain of pathnodes, starting at the node 'best_path'. For - * every pathnode found: - * (1) Create a corresponding plan node containing appropriate id, - * target list, and qualification information. - * (2) Modify qual clauses of join nodes so that subplan attributes are - * referenced using relative values. - * (3) Target lists are not modified, but will be in setrefs.c. + * every pathnode found, we create a corresponding plan node containing + * appropriate id, target list, and qualification information. + * + * The tlists and quals in the plan tree are still in planner format, + * ie, Vars still correspond to the parser's numbering. This will be + * fixed later by setrefs.c. * * best_path is the best access path * @@ -421,7 +421,8 @@ create_gating_plan(PlannerInfo *root, Plan *plan, List *quals) if (!pseudoconstants) return plan; - return (Plan *) make_result((List *) copyObject(plan->targetlist), + return (Plan *) make_result(root, + plan->targetlist, (Node *) pseudoconstants, plan); } @@ -519,7 +520,8 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path) if (best_path->subpaths == NIL) { /* Generate a Result plan with constant-FALSE gating qual */ - return (Plan *) make_result(tlist, + return (Plan *) make_result(root, + tlist, (Node *) list_make1(makeBoolConst(false, false)), NULL); @@ -559,7 +561,7 @@ create_result_plan(PlannerInfo *root, ResultPath *best_path) quals = order_qual_clauses(root, best_path->quals); - return make_result(tlist, (Node *) quals, NULL); + return make_result(root, tlist, (Node *) quals, NULL); } /* @@ -682,7 +684,7 @@ create_unique_plan(PlannerInfo *root, UniquePath *best_path) * node to help it along. */ if (!is_projection_capable_plan(subplan)) - subplan = (Plan *) make_result(newtlist, NULL, subplan); + subplan = (Plan *) make_result(root, newtlist, NULL, subplan); else subplan->targetlist = newtlist; } @@ -1065,12 +1067,6 @@ create_bitmap_scan_plan(PlannerInfo *root, */ bitmapqualorig = list_difference_ptr(bitmapqualorig, qpqual); - /* - * Copy the finished bitmapqualorig to make sure we have an independent - * copy --- needed in case there are subplans in the index quals - */ - bitmapqualorig = copyObject(bitmapqualorig); - /* Finally ready to build the plan node */ scan_plan = make_bitmap_heapscan(tlist, qpqual, @@ -1333,7 +1329,8 @@ create_subqueryscan_plan(PlannerInfo *root, Path *best_path, scan_plan = make_subqueryscan(tlist, scan_clauses, scan_relid, - best_path->parent->subplan); + best_path->parent->subplan, + best_path->parent->subrtable); copy_path_costsize(&scan_plan->scan.plan, best_path); @@ -2115,7 +2112,7 @@ order_qual_clauses(PlannerInfo *root, List *clauses) Node *clause = (Node *) lfirst(lc); QualCost qcost; - cost_qual_eval_node(&qcost, clause); + cost_qual_eval_node(&qcost, clause, root); items[i].clause = clause; items[i].cost = qcost.per_tuple; i++; @@ -2326,7 +2323,8 @@ SubqueryScan * make_subqueryscan(List *qptlist, List *qpqual, Index scanrelid, - Plan *subplan) + Plan *subplan, + List *subrtable) { SubqueryScan *node = makeNode(SubqueryScan); Plan *plan = &node->scan.plan; @@ -2345,6 +2343,7 @@ make_subqueryscan(List *qptlist, plan->righttree = NULL; node->scan.scanrelid = scanrelid; node->subplan = subplan; + node->subrtable = subrtable; return node; } @@ -2524,7 +2523,7 @@ make_hash(Plan *lefttree) * plan; this only affects EXPLAIN display not decisions. */ plan->startup_cost = plan->total_cost; - plan->targetlist = copyObject(lefttree->targetlist); + plan->targetlist = lefttree->targetlist; plan->qual = NIL; plan->lefttree = lefttree; plan->righttree = NULL; @@ -2583,7 +2582,7 @@ make_sort(PlannerInfo *root, Plan *lefttree, int numCols, lefttree->plan_width); plan->startup_cost = sort_path.startup_cost; plan->total_cost = sort_path.total_cost; - plan->targetlist = copyObject(lefttree->targetlist); + plan->targetlist = lefttree->targetlist; plan->qual = NIL; plan->lefttree = lefttree; plan->righttree = NULL; @@ -2741,10 +2740,7 @@ make_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys) * Do we need to insert a Result node? */ if (!is_projection_capable_plan(lefttree)) - { - tlist = copyObject(tlist); - lefttree = (Plan *) make_result(tlist, NULL, lefttree); - } + lefttree = (Plan *) make_result(root, tlist, NULL, lefttree); /* * Add resjunk entry to input's tlist @@ -2905,7 +2901,7 @@ make_material(Plan *lefttree) Plan *plan = &node->plan; /* cost should be inserted by caller */ - plan->targetlist = copyObject(lefttree->targetlist); + plan->targetlist = lefttree->targetlist; plan->qual = NIL; plan->lefttree = lefttree; plan->righttree = NULL; @@ -2996,12 +2992,12 @@ make_agg(PlannerInfo *root, List *tlist, List *qual, */ if (qual) { - cost_qual_eval(&qual_cost, qual); + cost_qual_eval(&qual_cost, qual, root); plan->startup_cost += qual_cost.startup; plan->total_cost += qual_cost.startup; plan->total_cost += qual_cost.per_tuple * plan->plan_rows; } - cost_qual_eval(&qual_cost, tlist); + cost_qual_eval(&qual_cost, tlist, root); plan->startup_cost += qual_cost.startup; plan->total_cost += qual_cost.startup; plan->total_cost += qual_cost.per_tuple * plan->plan_rows; @@ -3059,12 +3055,12 @@ make_group(PlannerInfo *root, */ if (qual) { - cost_qual_eval(&qual_cost, qual); + cost_qual_eval(&qual_cost, qual, root); plan->startup_cost += qual_cost.startup; plan->total_cost += qual_cost.startup; plan->total_cost += qual_cost.per_tuple * plan->plan_rows; } - cost_qual_eval(&qual_cost, tlist); + cost_qual_eval(&qual_cost, tlist, root); plan->startup_cost += qual_cost.startup; plan->total_cost += qual_cost.startup; plan->total_cost += qual_cost.per_tuple * plan->plan_rows; @@ -3108,7 +3104,7 @@ make_unique(Plan *lefttree, List *distinctList) * has a better idea. */ - plan->targetlist = copyObject(lefttree->targetlist); + plan->targetlist = lefttree->targetlist; plan->qual = NIL; plan->lefttree = lefttree; plan->righttree = NULL; @@ -3174,7 +3170,7 @@ make_setop(SetOpCmd cmd, Plan *lefttree, if (plan->plan_rows < 1) plan->plan_rows = 1; - plan->targetlist = copyObject(lefttree->targetlist); + plan->targetlist = lefttree->targetlist; plan->qual = NIL; plan->lefttree = lefttree; plan->righttree = NULL; @@ -3272,7 +3268,7 @@ make_limit(Plan *lefttree, Node *limitOffset, Node *limitCount, plan->plan_rows = 1; } - plan->targetlist = copyObject(lefttree->targetlist); + plan->targetlist = lefttree->targetlist; plan->qual = NIL; plan->lefttree = lefttree; plan->righttree = NULL; @@ -3293,7 +3289,8 @@ make_limit(Plan *lefttree, Node *limitOffset, Node *limitCount, * cost. In either case, tlist eval cost is not to be included here. */ Result * -make_result(List *tlist, +make_result(PlannerInfo *root, + List *tlist, Node *resconstantqual, Plan *subplan) { @@ -3312,7 +3309,7 @@ make_result(List *tlist, { QualCost qual_cost; - cost_qual_eval(&qual_cost, (List *) resconstantqual); + cost_qual_eval(&qual_cost, (List *) resconstantqual, root); /* resconstantqual is evaluated once at startup */ plan->startup_cost += qual_cost.startup + qual_cost.per_tuple; plan->total_cost += qual_cost.startup + qual_cost.per_tuple; diff --git a/src/backend/optimizer/plan/planagg.c b/src/backend/optimizer/plan/planagg.c index 406cc9dd49665845ef9d889565dbd1226c72b236..5411072b8dbb5f15e2c365f874b817ebc21bd197 100644 --- a/src/backend/optimizer/plan/planagg.c +++ b/src/backend/optimizer/plan/planagg.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/optimizer/plan/planagg.c,v 1.28 2007/02/20 17:32:15 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/plan/planagg.c,v 1.29 2007/02/22 22:00:24 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -188,10 +188,10 @@ optimize_minmax_aggregates(PlannerInfo *root, List *tlist, Path *best_path) /* * Generate the output plan --- basically just a Result */ - plan = (Plan *) make_result(tlist, hqual, NULL); + plan = (Plan *) make_result(root, tlist, hqual, NULL); /* Account for evaluation cost of the tlist (make_result did the rest) */ - cost_qual_eval(&tlist_cost, tlist); + cost_qual_eval(&tlist_cost, tlist, root); plan->startup_cost += tlist_cost.startup; plan->total_cost += tlist_cost.startup + tlist_cost.per_tuple; diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index 3bb603a0f614bb53ff4cbb0eebefee770afbd819..b45288dc5b8faafe9a827aacb56fbcccd69c6a0c 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/optimizer/plan/planner.c,v 1.214 2007/02/20 17:32:15 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/plan/planner.c,v 1.215 2007/02/22 22:00:24 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -88,6 +88,8 @@ planner(Query *parse, bool isCursor, int cursorOptions, double tuple_fraction; PlannerInfo *root; Plan *top_plan; + ListCell *lp, + *lr; /* * Set up global state for this planner invocation. This data is needed @@ -99,7 +101,9 @@ planner(Query *parse, bool isCursor, int cursorOptions, glob->boundParams = boundParams; glob->paramlist = NIL; - glob->next_plan_id = 0; + glob->subplans = NIL; + glob->subrtables = NIL; + glob->finalrtable = NIL; /* Determine what fraction of the plan is likely to be scanned */ if (isCursor) @@ -132,7 +136,17 @@ planner(Query *parse, bool isCursor, int cursorOptions, } /* final cleanup of the plan */ - top_plan = set_plan_references(top_plan, parse->rtable); + Assert(glob->finalrtable == NIL); + top_plan = set_plan_references(glob, top_plan, root->parse->rtable); + /* ... and the subplans (both regular subplans and initplans) */ + Assert(list_length(glob->subplans) == list_length(glob->subrtables)); + forboth(lp, glob->subplans, lr, glob->subrtables) + { + Plan *subplan = (Plan *) lfirst(lp); + List *subrtable = (List *) lfirst(lr); + + lfirst(lp) = set_plan_references(glob, subplan, subrtable); + } /* build the PlannedStmt result */ result = makeNode(PlannedStmt); @@ -140,9 +154,10 @@ planner(Query *parse, bool isCursor, int cursorOptions, result->commandType = parse->commandType; result->canSetTag = parse->canSetTag; result->planTree = top_plan; - result->rtable = parse->rtable; + result->rtable = glob->finalrtable; result->resultRelations = root->resultRelations; result->into = parse->into; + result->subplans = glob->subplans; result->returningLists = root->returningLists; result->rowMarks = parse->rowMarks; result->nParamExec = list_length(glob->paramlist); @@ -182,7 +197,7 @@ subquery_planner(PlannerGlobal *glob, Query *parse, Index level, double tuple_fraction, PlannerInfo **subroot) { - int saved_plan_id = glob->next_plan_id; + int num_old_subplans = list_length(glob->subplans); PlannerInfo *root; Plan *plan; List *newHaving; @@ -384,7 +399,8 @@ subquery_planner(PlannerGlobal *glob, Query *parse, * initPlan list and extParam/allParam sets for plan nodes, and attach the * initPlans to the top plan node. */ - if (root->glob->next_plan_id != saved_plan_id || root->query_level > 1) + if (list_length(glob->subplans) != num_old_subplans || + root->query_level > 1) SS_finalize_plan(root, plan); /* Return internal info if caller wants it */ @@ -621,7 +637,8 @@ inheritance_planner(PlannerInfo *root) * If we managed to exclude every child rel, return a dummy plan */ if (subplans == NIL) - return (Plan *) make_result(tlist, + return (Plan *) make_result(root, + tlist, (Node *) list_make1(makeBoolConst(false, false)), NULL); @@ -639,6 +656,10 @@ inheritance_planner(PlannerInfo *root) */ parse->rtable = rtable; + /* Suppress Append if there's only one surviving child rel */ + if (list_length(subplans) == 1) + return (Plan *) linitial(subplans); + return (Plan *) make_append(subplans, true, tlist); } @@ -897,7 +918,9 @@ grouping_planner(PlannerInfo *root, double tuple_fraction) */ if (!is_projection_capable_plan(result_plan)) { - result_plan = (Plan *) make_result(sub_tlist, NULL, + result_plan = (Plan *) make_result(root, + sub_tlist, + NULL, result_plan); } else @@ -928,7 +951,7 @@ grouping_planner(PlannerInfo *root, double tuple_fraction) * tuples) --- so make_agg() and make_group() are responsible * for computing the added cost. */ - cost_qual_eval(&tlist_cost, sub_tlist); + cost_qual_eval(&tlist_cost, sub_tlist, root); result_plan->startup_cost += tlist_cost.startup; result_plan->total_cost += tlist_cost.startup + tlist_cost.per_tuple * result_plan->plan_rows; @@ -1051,7 +1074,8 @@ grouping_planner(PlannerInfo *root, double tuple_fraction) * this routine to avoid having to generate the plan in the * first place. */ - result_plan = (Plan *) make_result(tlist, + result_plan = (Plan *) make_result(root, + tlist, parse->havingQual, NULL); } @@ -1110,6 +1134,7 @@ grouping_planner(PlannerInfo *root, double tuple_fraction) { List *rlist; + Assert(parse->resultRelation); rlist = set_returning_clause_references(parse->returningList, result_plan, parse->resultRelation); @@ -1544,7 +1569,7 @@ choose_hashed_grouping(PlannerInfo *root, double tuple_fraction, * pass down only c,d,a+b, but it's not really worth the trouble to * eliminate simple var references from the subplan. We will avoid doing * the extra computation to recompute a+b at the outer level; see - * replace_vars_with_subplan_refs() in setrefs.c.) + * fix_upper_expr() in setrefs.c.) * * If we are grouping or aggregating, *and* there are no non-Var grouping * expressions, then the returned tlist is effectively dummy; we do not diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c index 3d9f5486bccfce75f312eee1be38fb6b7ec90a75..aaa383742e7bdb2b798f72920a934aaec2176dff 100644 --- a/src/backend/optimizer/plan/setrefs.c +++ b/src/backend/optimizer/plan/setrefs.c @@ -9,7 +9,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/optimizer/plan/setrefs.c,v 1.130 2007/02/19 02:23:12 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/plan/setrefs.c,v 1.131 2007/02/22 22:00:24 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -40,48 +40,60 @@ typedef struct tlist_vinfo vars[1]; /* VARIABLE LENGTH ARRAY */ } indexed_tlist; /* VARIABLE LENGTH STRUCT */ +typedef struct +{ + int rtoffset; +} fix_scan_expr_context; + typedef struct { indexed_tlist *outer_itlist; indexed_tlist *inner_itlist; Index acceptable_rel; -} join_references_context; + int rtoffset; +} fix_join_expr_context; typedef struct { indexed_tlist *subplan_itlist; Index subvarno; -} replace_vars_with_subplan_refs_context; + int rtoffset; +} fix_upper_expr_context; + +#define fix_scan_list(lst, rtoffset) \ + ((List *) fix_scan_expr((Node *) (lst), rtoffset)) -static Plan *set_subqueryscan_references(SubqueryScan *plan, List *rtable); +static Plan *set_plan_refs(PlannerGlobal *glob, Plan *plan, int rtoffset); +static Plan *set_subqueryscan_references(PlannerGlobal *glob, + SubqueryScan *plan, + int rtoffset); static bool trivial_subqueryscan(SubqueryScan *plan); -static void adjust_plan_varnos(Plan *plan, int rtoffset); -static void adjust_expr_varnos(Node *node, int rtoffset); -static bool adjust_expr_varnos_walker(Node *node, int *context); -static void fix_expr_references(Plan *plan, Node *node); -static bool fix_expr_references_walker(Node *node, void *context); -static void set_join_references(Join *join); +static Node *fix_scan_expr(Node *node, int rtoffset); +static Node *fix_scan_expr_mutator(Node *node, fix_scan_expr_context *context); +static void set_join_references(Join *join, int rtoffset); static void set_inner_join_references(Plan *inner_plan, indexed_tlist *outer_itlist); -static void set_uppernode_references(Plan *plan, Index subvarno); +static void set_upper_references(Plan *plan, Index subvarno, int rtoffset); static indexed_tlist *build_tlist_index(List *tlist); static Var *search_indexed_tlist_for_var(Var *var, indexed_tlist *itlist, - Index newvarno); + Index newvarno, + int rtoffset); static Var *search_indexed_tlist_for_non_var(Node *node, indexed_tlist *itlist, Index newvarno); -static List *join_references(List *clauses, - indexed_tlist *outer_itlist, - indexed_tlist *inner_itlist, - Index acceptable_rel); -static Node *join_references_mutator(Node *node, - join_references_context *context); -static Node *replace_vars_with_subplan_refs(Node *node, - indexed_tlist *subplan_itlist, - Index subvarno); -static Node *replace_vars_with_subplan_refs_mutator(Node *node, - replace_vars_with_subplan_refs_context *context); +static List *fix_join_expr(List *clauses, + indexed_tlist *outer_itlist, + indexed_tlist *inner_itlist, + Index acceptable_rel, int rtoffset); +static Node *fix_join_expr_mutator(Node *node, + fix_join_expr_context *context); +static Node *fix_upper_expr(Node *node, + indexed_tlist *subplan_itlist, + Index subvarno, + int rtoffset); +static Node *fix_upper_expr_mutator(Node *node, + fix_upper_expr_context *context); static bool fix_opfuncids_walker(Node *node, void *context); @@ -96,34 +108,87 @@ static bool fix_opfuncids_walker(Node *node, void *context); * * This is the final processing pass of the planner/optimizer. The plan * tree is complete; we just have to adjust some representational details - * for the convenience of the executor. We update Vars in upper plan nodes - * to refer to the outputs of their subplans, and we compute regproc OIDs - * for operators (ie, we look up the function that implements each op). + * for the convenience of the executor: + * + * 1. We flatten the various subquery rangetables into a single list, and + * zero out RangeTblEntry fields that are not useful to the executor. + * + * 2. We adjust Vars in scan nodes to be consistent with the flat rangetable. + * + * 3. We adjust Vars in upper plan nodes to refer to the outputs of their + * subplans. + * + * 4. We compute regproc OIDs for operators (ie, we look up the function + * that implements each op). * * We also perform one final optimization step, which is to delete * SubqueryScan plan nodes that aren't doing anything useful (ie, have * no qual and a no-op targetlist). The reason for doing this last is that * it can't readily be done before set_plan_references, because it would - * break set_uppernode_references: the Vars in the subquery's top tlist - * won't match up with the Vars in the outer plan tree. The SubqueryScan + * break set_upper_references: the Vars in the subquery's top tlist + * wouldn't match up with the Vars in the outer plan tree. The SubqueryScan * serves a necessary function as a buffer between outer query and subquery - * variable numbering ... but the executor doesn't care about that, only the - * planner. + * variable numbering ... but after we've flattened the rangetable this is + * no longer a problem, since there's only one rtindex namespace. * * set_plan_references recursively traverses the whole plan tree. * + * Inputs: + * glob: global data for planner run + * plan: the topmost node of the plan + * rtable: the rangetable for the current subquery + * * The return value is normally the same Plan node passed in, but can be * different when the passed-in Plan is a SubqueryScan we decide isn't needed. * - * Note: to delete a SubqueryScan, we have to renumber Vars in its child nodes - * and append the modified subquery rangetable to the outer rangetable. - * Therefore "rtable" is an in/out argument and really should be declared - * "List **". But in the interest of notational simplicity we don't do that. - * (Since rtable can't be NIL if there's a SubqueryScan, the list header - * address won't change when we append a subquery rangetable.) + * The flattened rangetable entries are appended to glob->finalrtable. + * + * Notice that we modify Plan nodes in-place, but use expression_tree_mutator + * to process targetlist and qual expressions. We can assume that the Plan + * nodes were just built by the planner and are not multiply referenced, but + * it's not so safe to assume that for expression tree nodes. */ Plan * -set_plan_references(Plan *plan, List *rtable) +set_plan_references(PlannerGlobal *glob, Plan *plan, List *rtable) +{ + int rtoffset = list_length(glob->finalrtable); + ListCell *lc; + + /* + * In the flat rangetable, we zero out substructure pointers that are + * not needed by the executor; this reduces the storage space and + * copying cost for cached plans. + */ + foreach(lc, rtable) + { + RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc); + RangeTblEntry *newrte; + + /* flat copy to duplicate all the scalar fields */ + newrte = (RangeTblEntry *) palloc(sizeof(RangeTblEntry)); + memcpy(newrte, rte, sizeof(RangeTblEntry)); + + /* zap unneeded sub-structure (we keep only the eref Alias) */ + newrte->subquery = NULL; + newrte->funcexpr = NULL; + newrte->funccoltypes = NIL; + newrte->funccoltypmods = NIL; + newrte->values_lists = NIL; + newrte->joinaliasvars = NIL; + newrte->alias = NULL; + + glob->finalrtable = lappend(glob->finalrtable, newrte); + } + + /* Now fix the Plan tree */ + return set_plan_refs(glob, plan, rtoffset); +} + +/* + * set_plan_refs: recurse through the Plan nodes of a single subquery level + */ +static Plan * +set_plan_refs(PlannerGlobal *glob, Plan *plan, int rtoffset) { ListCell *l; @@ -136,73 +201,109 @@ set_plan_references(Plan *plan, List *rtable) switch (nodeTag(plan)) { case T_SeqScan: - fix_expr_references(plan, (Node *) plan->targetlist); - fix_expr_references(plan, (Node *) plan->qual); + { + SeqScan *splan = (SeqScan *) plan; + + splan->scanrelid += rtoffset; + splan->plan.targetlist = + fix_scan_list(splan->plan.targetlist, rtoffset); + splan->plan.qual = + fix_scan_list(splan->plan.qual, rtoffset); + } break; case T_IndexScan: - fix_expr_references(plan, (Node *) plan->targetlist); - fix_expr_references(plan, (Node *) plan->qual); - fix_expr_references(plan, - (Node *) ((IndexScan *) plan)->indexqual); - fix_expr_references(plan, - (Node *) ((IndexScan *) plan)->indexqualorig); + { + IndexScan *splan = (IndexScan *) plan; + + splan->scan.scanrelid += rtoffset; + splan->scan.plan.targetlist = + fix_scan_list(splan->scan.plan.targetlist, rtoffset); + splan->scan.plan.qual = + fix_scan_list(splan->scan.plan.qual, rtoffset); + splan->indexqual = + fix_scan_list(splan->indexqual, rtoffset); + splan->indexqualorig = + fix_scan_list(splan->indexqualorig, rtoffset); + } break; case T_BitmapIndexScan: - /* no need to fix targetlist and qual */ - Assert(plan->targetlist == NIL); - Assert(plan->qual == NIL); - fix_expr_references(plan, - (Node *) ((BitmapIndexScan *) plan)->indexqual); - fix_expr_references(plan, - (Node *) ((BitmapIndexScan *) plan)->indexqualorig); + { + BitmapIndexScan *splan = (BitmapIndexScan *) plan; + + splan->scan.scanrelid += rtoffset; + /* no need to fix targetlist and qual */ + Assert(splan->scan.plan.targetlist == NIL); + Assert(splan->scan.plan.qual == NIL); + splan->indexqual = + fix_scan_list(splan->indexqual, rtoffset); + splan->indexqualorig = + fix_scan_list(splan->indexqualorig, rtoffset); + } break; case T_BitmapHeapScan: - fix_expr_references(plan, (Node *) plan->targetlist); - fix_expr_references(plan, (Node *) plan->qual); - fix_expr_references(plan, - (Node *) ((BitmapHeapScan *) plan)->bitmapqualorig); + { + BitmapHeapScan *splan = (BitmapHeapScan *) plan; + + splan->scan.scanrelid += rtoffset; + splan->scan.plan.targetlist = + fix_scan_list(splan->scan.plan.targetlist, rtoffset); + splan->scan.plan.qual = + fix_scan_list(splan->scan.plan.qual, rtoffset); + splan->bitmapqualorig = + fix_scan_list(splan->bitmapqualorig, rtoffset); + } break; case T_TidScan: - fix_expr_references(plan, (Node *) plan->targetlist); - fix_expr_references(plan, (Node *) plan->qual); - fix_expr_references(plan, (Node *) ((TidScan *) plan)->tidquals); + { + TidScan *splan = (TidScan *) plan; + + splan->scan.scanrelid += rtoffset; + splan->scan.plan.targetlist = + fix_scan_list(splan->scan.plan.targetlist, rtoffset); + splan->scan.plan.qual = + fix_scan_list(splan->scan.plan.qual, rtoffset); + splan->tidquals = + fix_scan_list(splan->tidquals, rtoffset); + } break; case T_SubqueryScan: /* Needs special treatment, see comments below */ - return set_subqueryscan_references((SubqueryScan *) plan, rtable); + return set_subqueryscan_references(glob, + (SubqueryScan *) plan, + rtoffset); case T_FunctionScan: - fix_expr_references(plan, (Node *) plan->targetlist); - fix_expr_references(plan, (Node *) plan->qual); - fix_expr_references(plan, ((FunctionScan *) plan)->funcexpr); + { + FunctionScan *splan = (FunctionScan *) plan; + + splan->scan.scanrelid += rtoffset; + splan->scan.plan.targetlist = + fix_scan_list(splan->scan.plan.targetlist, rtoffset); + splan->scan.plan.qual = + fix_scan_list(splan->scan.plan.qual, rtoffset); + splan->funcexpr = + fix_scan_expr(splan->funcexpr, rtoffset); + } break; case T_ValuesScan: - fix_expr_references(plan, (Node *) plan->targetlist); - fix_expr_references(plan, (Node *) plan->qual); - fix_expr_references(plan, - (Node *) ((ValuesScan *) plan)->values_lists); + { + ValuesScan *splan = (ValuesScan *) plan; + + splan->scan.scanrelid += rtoffset; + splan->scan.plan.targetlist = + fix_scan_list(splan->scan.plan.targetlist, rtoffset); + splan->scan.plan.qual = + fix_scan_list(splan->scan.plan.qual, rtoffset); + splan->values_lists = + fix_scan_list(splan->values_lists, rtoffset); + } break; + case T_NestLoop: - set_join_references((Join *) plan); - fix_expr_references(plan, (Node *) plan->targetlist); - fix_expr_references(plan, (Node *) plan->qual); - fix_expr_references(plan, (Node *) ((Join *) plan)->joinqual); - break; case T_MergeJoin: - set_join_references((Join *) plan); - fix_expr_references(plan, (Node *) plan->targetlist); - fix_expr_references(plan, (Node *) plan->qual); - fix_expr_references(plan, (Node *) ((Join *) plan)->joinqual); - fix_expr_references(plan, - (Node *) ((MergeJoin *) plan)->mergeclauses); - break; case T_HashJoin: - set_join_references((Join *) plan); - fix_expr_references(plan, (Node *) plan->targetlist); - fix_expr_references(plan, (Node *) plan->qual); - fix_expr_references(plan, (Node *) ((Join *) plan)->joinqual); - fix_expr_references(plan, - (Node *) ((HashJoin *) plan)->hashclauses); + set_join_references((Join *) plan, rtoffset); break; + case T_Hash: case T_Material: case T_Sort: @@ -211,75 +312,113 @@ set_plan_references(Plan *plan, List *rtable) /* * These plan types don't actually bother to evaluate their - * targetlists (because they just return their unmodified input - * tuples). The optimizer is lazy about creating really valid - * targetlists for them --- it tends to just put in a pointer to - * the child plan node's tlist. Hence, we leave the tlist alone. - * In particular, we do not want to process subplans in the tlist, - * since we will likely end up reprocessing subplans that also - * appear in lower levels of the plan tree! - * + * targetlists, because they just return their unmodified input + * tuples. Even though the targetlist won't be used by the + * executor, we fix it up for possible use by EXPLAIN (not to + * mention ease of debugging --- wrong varnos are very confusing). + */ + plan->targetlist = + fix_scan_list(plan->targetlist, rtoffset); + /* * Since these plan types don't check quals either, we should not * find any qual expression attached to them. */ Assert(plan->qual == NIL); break; case T_Limit: - - /* - * Like the plan types above, Limit doesn't evaluate its tlist or - * quals. It does have live expressions for limit/offset, - * however. - */ - Assert(plan->qual == NIL); - fix_expr_references(plan, ((Limit *) plan)->limitOffset); - fix_expr_references(plan, ((Limit *) plan)->limitCount); + { + Limit *splan = (Limit *) plan; + + /* + * Like the plan types above, Limit doesn't evaluate its tlist + * or quals. It does have live expressions for limit/offset, + * however. + */ + splan->plan.targetlist = + fix_scan_list(splan->plan.targetlist, rtoffset); + Assert(splan->plan.qual == NIL); + splan->limitOffset = + fix_scan_expr(splan->limitOffset, rtoffset); + splan->limitCount = + fix_scan_expr(splan->limitCount, rtoffset); + } break; case T_Agg: case T_Group: - set_uppernode_references(plan, (Index) 0); - fix_expr_references(plan, (Node *) plan->targetlist); - fix_expr_references(plan, (Node *) plan->qual); + set_upper_references(plan, (Index) 0, rtoffset); break; case T_Result: - - /* - * Result may or may not have a subplan; no need to fix up subplan - * references if it hasn't got one... - * - * XXX why does Result use a different subvarno from Agg/Group? - */ - if (plan->lefttree != NULL) - set_uppernode_references(plan, (Index) OUTER); - fix_expr_references(plan, (Node *) plan->targetlist); - fix_expr_references(plan, (Node *) plan->qual); - fix_expr_references(plan, ((Result *) plan)->resconstantqual); + { + Result *splan = (Result *) plan; + + /* + * Result may or may not have a subplan; if not, it's more + * like a scan node than an upper node. + * + * XXX why does Result use a different subvarno from Agg/Group? + */ + if (splan->plan.lefttree != NULL) + set_upper_references(plan, (Index) OUTER, rtoffset); + else + { + splan->plan.targetlist = + fix_scan_list(splan->plan.targetlist, rtoffset); + splan->plan.qual = + fix_scan_list(splan->plan.qual, rtoffset); + } + /* resconstantqual can't contain any subplan variable refs */ + splan->resconstantqual = + fix_scan_expr(splan->resconstantqual, rtoffset); + } break; case T_Append: - - /* - * Append, like Sort et al, doesn't actually evaluate its - * targetlist or check quals, and we haven't bothered to give it - * its own tlist copy. So, don't fix targetlist/qual. But do - * recurse into child plans. - */ - Assert(plan->qual == NIL); - foreach(l, ((Append *) plan)->appendplans) - lfirst(l) = set_plan_references((Plan *) lfirst(l), rtable); + { + Append *splan = (Append *) plan; + + /* + * Append, like Sort et al, doesn't actually evaluate its + * targetlist or check quals. + */ + splan->plan.targetlist = + fix_scan_list(splan->plan.targetlist, rtoffset); + Assert(splan->plan.qual == NIL); + foreach(l, splan->appendplans) + { + lfirst(l) = set_plan_refs(glob, + (Plan *) lfirst(l), + rtoffset); + } + } break; case T_BitmapAnd: - /* BitmapAnd works like Append, but has no tlist */ - Assert(plan->targetlist == NIL); - Assert(plan->qual == NIL); - foreach(l, ((BitmapAnd *) plan)->bitmapplans) - lfirst(l) = set_plan_references((Plan *) lfirst(l), rtable); + { + BitmapAnd *splan = (BitmapAnd *) plan; + + /* BitmapAnd works like Append, but has no tlist */ + Assert(splan->plan.targetlist == NIL); + Assert(splan->plan.qual == NIL); + foreach(l, splan->bitmapplans) + { + lfirst(l) = set_plan_refs(glob, + (Plan *) lfirst(l), + rtoffset); + } + } break; case T_BitmapOr: - /* BitmapOr works like Append, but has no tlist */ - Assert(plan->targetlist == NIL); - Assert(plan->qual == NIL); - foreach(l, ((BitmapOr *) plan)->bitmapplans) - lfirst(l) = set_plan_references((Plan *) lfirst(l), rtable); + { + BitmapOr *splan = (BitmapOr *) plan; + + /* BitmapOr works like Append, but has no tlist */ + Assert(splan->plan.targetlist == NIL); + Assert(splan->plan.qual == NIL); + foreach(l, splan->bitmapplans) + { + lfirst(l) = set_plan_refs(glob, + (Plan *) lfirst(l), + rtoffset); + } + } break; default: elog(ERROR, "unrecognized node type: %d", @@ -288,25 +427,15 @@ set_plan_references(Plan *plan, List *rtable) } /* - * Now recurse into child plans and initplans, if any + * Now recurse into child plans, if any * * NOTE: it is essential that we recurse into child plans AFTER we set * subplan references in this plan's tlist and quals. If we did the * reference-adjustments bottom-up, then we would fail to match this * plan's var nodes against the already-modified nodes of the children. - * Fortunately, that consideration doesn't apply to SubPlan nodes; else - * we'd need two passes over the expression trees. */ - plan->lefttree = set_plan_references(plan->lefttree, rtable); - plan->righttree = set_plan_references(plan->righttree, rtable); - - foreach(l, plan->initPlan) - { - SubPlan *sp = (SubPlan *) lfirst(l); - - Assert(IsA(sp, SubPlan)); - sp->plan = set_plan_references(sp->plan, sp->rtable); - } + plan->lefttree = set_plan_refs(glob, plan->lefttree, rtoffset); + plan->righttree = set_plan_refs(glob, plan->righttree, rtoffset); return plan; } @@ -319,54 +448,29 @@ set_plan_references(Plan *plan, List *rtable) * to do the normal processing on it. */ static Plan * -set_subqueryscan_references(SubqueryScan *plan, List *rtable) +set_subqueryscan_references(PlannerGlobal *glob, + SubqueryScan *plan, + int rtoffset) { Plan *result; - RangeTblEntry *rte; - ListCell *l; /* First, recursively process the subplan */ - rte = rt_fetch(plan->scan.scanrelid, rtable); - Assert(rte->rtekind == RTE_SUBQUERY); - plan->subplan = set_plan_references(plan->subplan, - rte->subquery->rtable); - - /* - * We have to process any initplans too; set_plan_references can't do it - * for us because of the possibility of double-processing. - */ - foreach(l, plan->scan.plan.initPlan) - { - SubPlan *sp = (SubPlan *) lfirst(l); + plan->subplan = set_plan_references(glob, plan->subplan, plan->subrtable); - Assert(IsA(sp, SubPlan)); - sp->plan = set_plan_references(sp->plan, sp->rtable); - } + /* subrtable is no longer needed in the plan tree */ + plan->subrtable = NIL; if (trivial_subqueryscan(plan)) { /* - * We can omit the SubqueryScan node and just pull up the subplan. We - * have to merge its rtable into the outer rtable, which means - * adjusting varnos throughout the subtree. + * We can omit the SubqueryScan node and just pull up the subplan. */ - int rtoffset = list_length(rtable); - List *sub_rtable; ListCell *lp, *lc; - sub_rtable = copyObject(rte->subquery->rtable); - rtable = list_concat(rtable, sub_rtable); - - /* - * we have to copy the subplan to make sure there are no duplicately - * linked nodes in it, else adjust_plan_varnos might increment some - * varnos twice - */ - result = copyObject(plan->subplan); - - adjust_plan_varnos(result, rtoffset); + result = plan->subplan; + /* We have to be sure we don't lose any initplans */ result->initPlan = list_concat(plan->scan.plan.initPlan, result->initPlan); @@ -391,14 +495,17 @@ set_subqueryscan_references(SubqueryScan *plan, List *rtable) /* * Keep the SubqueryScan node. We have to do the processing that * set_plan_references would otherwise have done on it. Notice we do - * not do set_uppernode_references() here, because a SubqueryScan will + * not do set_upper_references() here, because a SubqueryScan will * always have been created with correct references to its subplan's * outputs to begin with. */ - result = (Plan *) plan; + plan->scan.scanrelid += rtoffset; + plan->scan.plan.targetlist = + fix_scan_list(plan->scan.plan.targetlist, rtoffset); + plan->scan.plan.qual = + fix_scan_list(plan->scan.plan.qual, rtoffset); - fix_expr_references(result, (Node *) result->targetlist); - fix_expr_references(result, (Node *) result->qual); + result = (Plan *) plan; } return result; @@ -463,261 +570,72 @@ trivial_subqueryscan(SubqueryScan *plan) } /* - * adjust_plan_varnos - * Offset varnos and other rangetable indexes in a plan tree by rtoffset. - */ -static void -adjust_plan_varnos(Plan *plan, int rtoffset) -{ - ListCell *l; - - if (plan == NULL) - return; - - /* - * Plan-type-specific fixes - */ - switch (nodeTag(plan)) - { - case T_SeqScan: - ((SeqScan *) plan)->scanrelid += rtoffset; - adjust_expr_varnos((Node *) plan->targetlist, rtoffset); - adjust_expr_varnos((Node *) plan->qual, rtoffset); - break; - case T_IndexScan: - ((IndexScan *) plan)->scan.scanrelid += rtoffset; - adjust_expr_varnos((Node *) plan->targetlist, rtoffset); - adjust_expr_varnos((Node *) plan->qual, rtoffset); - adjust_expr_varnos((Node *) ((IndexScan *) plan)->indexqual, - rtoffset); - adjust_expr_varnos((Node *) ((IndexScan *) plan)->indexqualorig, - rtoffset); - break; - case T_BitmapIndexScan: - ((BitmapIndexScan *) plan)->scan.scanrelid += rtoffset; - /* no need to fix targetlist and qual */ - Assert(plan->targetlist == NIL); - Assert(plan->qual == NIL); - adjust_expr_varnos((Node *) ((BitmapIndexScan *) plan)->indexqual, - rtoffset); - adjust_expr_varnos((Node *) ((BitmapIndexScan *) plan)->indexqualorig, - rtoffset); - break; - case T_BitmapHeapScan: - ((BitmapHeapScan *) plan)->scan.scanrelid += rtoffset; - adjust_expr_varnos((Node *) plan->targetlist, rtoffset); - adjust_expr_varnos((Node *) plan->qual, rtoffset); - adjust_expr_varnos((Node *) ((BitmapHeapScan *) plan)->bitmapqualorig, - rtoffset); - break; - case T_TidScan: - ((TidScan *) plan)->scan.scanrelid += rtoffset; - adjust_expr_varnos((Node *) plan->targetlist, rtoffset); - adjust_expr_varnos((Node *) plan->qual, rtoffset); - adjust_expr_varnos((Node *) ((TidScan *) plan)->tidquals, - rtoffset); - break; - case T_SubqueryScan: - ((SubqueryScan *) plan)->scan.scanrelid += rtoffset; - adjust_expr_varnos((Node *) plan->targetlist, rtoffset); - adjust_expr_varnos((Node *) plan->qual, rtoffset); - /* we should not recurse into the subquery! */ - break; - case T_FunctionScan: - ((FunctionScan *) plan)->scan.scanrelid += rtoffset; - adjust_expr_varnos((Node *) plan->targetlist, rtoffset); - adjust_expr_varnos((Node *) plan->qual, rtoffset); - adjust_expr_varnos(((FunctionScan *) plan)->funcexpr, - rtoffset); - break; - case T_ValuesScan: - ((ValuesScan *) plan)->scan.scanrelid += rtoffset; - adjust_expr_varnos((Node *) plan->targetlist, rtoffset); - adjust_expr_varnos((Node *) plan->qual, rtoffset); - adjust_expr_varnos((Node *) ((ValuesScan *) plan)->values_lists, - rtoffset); - break; - case T_NestLoop: - adjust_expr_varnos((Node *) plan->targetlist, rtoffset); - adjust_expr_varnos((Node *) plan->qual, rtoffset); - adjust_expr_varnos((Node *) ((Join *) plan)->joinqual, rtoffset); - break; - case T_MergeJoin: - adjust_expr_varnos((Node *) plan->targetlist, rtoffset); - adjust_expr_varnos((Node *) plan->qual, rtoffset); - adjust_expr_varnos((Node *) ((Join *) plan)->joinqual, rtoffset); - adjust_expr_varnos((Node *) ((MergeJoin *) plan)->mergeclauses, - rtoffset); - break; - case T_HashJoin: - adjust_expr_varnos((Node *) plan->targetlist, rtoffset); - adjust_expr_varnos((Node *) plan->qual, rtoffset); - adjust_expr_varnos((Node *) ((Join *) plan)->joinqual, rtoffset); - adjust_expr_varnos((Node *) ((HashJoin *) plan)->hashclauses, - rtoffset); - break; - case T_Hash: - case T_Material: - case T_Sort: - case T_Unique: - case T_SetOp: - - /* - * Even though the targetlist won't be used by the executor, we - * fix it up for possible use by EXPLAIN (not to mention ease of - * debugging --- wrong varnos are very confusing). - */ - adjust_expr_varnos((Node *) plan->targetlist, rtoffset); - Assert(plan->qual == NIL); - break; - case T_Limit: - - /* - * Like the plan types above, Limit doesn't evaluate its tlist or - * quals. It does have live expressions for limit/offset, - * however. - */ - adjust_expr_varnos((Node *) plan->targetlist, rtoffset); - Assert(plan->qual == NIL); - adjust_expr_varnos(((Limit *) plan)->limitOffset, rtoffset); - adjust_expr_varnos(((Limit *) plan)->limitCount, rtoffset); - break; - case T_Agg: - case T_Group: - adjust_expr_varnos((Node *) plan->targetlist, rtoffset); - adjust_expr_varnos((Node *) plan->qual, rtoffset); - break; - case T_Result: - adjust_expr_varnos((Node *) plan->targetlist, rtoffset); - adjust_expr_varnos((Node *) plan->qual, rtoffset); - adjust_expr_varnos(((Result *) plan)->resconstantqual, rtoffset); - break; - case T_Append: - adjust_expr_varnos((Node *) plan->targetlist, rtoffset); - Assert(plan->qual == NIL); - foreach(l, ((Append *) plan)->appendplans) - adjust_plan_varnos((Plan *) lfirst(l), rtoffset); - break; - case T_BitmapAnd: - /* BitmapAnd works like Append, but has no tlist */ - Assert(plan->targetlist == NIL); - Assert(plan->qual == NIL); - foreach(l, ((BitmapAnd *) plan)->bitmapplans) - adjust_plan_varnos((Plan *) lfirst(l), rtoffset); - break; - case T_BitmapOr: - /* BitmapOr works like Append, but has no tlist */ - Assert(plan->targetlist == NIL); - Assert(plan->qual == NIL); - foreach(l, ((BitmapOr *) plan)->bitmapplans) - adjust_plan_varnos((Plan *) lfirst(l), rtoffset); - break; - default: - elog(ERROR, "unrecognized node type: %d", - (int) nodeTag(plan)); - break; - } - - /* - * Now recurse into child plans. - * - * We don't need to (and in fact mustn't) recurse into subqueries, so no - * need to examine initPlan list. - */ - adjust_plan_varnos(plan->lefttree, rtoffset); - adjust_plan_varnos(plan->righttree, rtoffset); -} - -/* - * adjust_expr_varnos - * Offset varnos of Vars in an expression by rtoffset. + * fix_scan_expr + * Do set_plan_references processing on a scan-level expression * - * This is different from the rewriter's OffsetVarNodes in that it has to - * work on an already-planned expression tree; in particular, we should not - * disturb INNER and OUTER references. On the other hand, we don't have to - * recurse into subqueries nor deal with outer-level Vars, so it's pretty - * simple. + * This consists of incrementing all Vars' varnos by rtoffset and + * looking up operator opcode info for OpExpr and related nodes. */ -static void -adjust_expr_varnos(Node *node, int rtoffset) +static Node * +fix_scan_expr(Node *node, int rtoffset) { - /* This tree walk requires no special setup, so away we go... */ - adjust_expr_varnos_walker(node, &rtoffset); + fix_scan_expr_context context; + + context.rtoffset = rtoffset; + return fix_scan_expr_mutator(node, &context); } -static bool -adjust_expr_varnos_walker(Node *node, int *context) +static Node * +fix_scan_expr_mutator(Node *node, fix_scan_expr_context *context) { if (node == NULL) - return false; + return NULL; if (IsA(node, Var)) { - Var *var = (Var *) node; + Var *var = (Var *) copyObject(node); Assert(var->varlevelsup == 0); - if (var->varno > 0 && var->varno != INNER && var->varno != OUTER) - var->varno += *context; + /* + * We should not see any Vars marked INNER, but in a nestloop inner + * scan there could be OUTER Vars. Leave them alone. + */ + Assert(var->varno != INNER); + if (var->varno > 0 && var->varno != OUTER) + var->varno += context->rtoffset; if (var->varnoold > 0) - var->varnoold += *context; - return false; + var->varnoold += context->rtoffset; + return (Node *) var; } - return expression_tree_walker(node, adjust_expr_varnos_walker, - (void *) context); -} - -/* - * fix_expr_references - * Do final cleanup on expressions (targetlists or quals). - * - * This consists of looking up operator opcode info for OpExpr nodes - * and recursively performing set_plan_references on subplans. - * - * The Plan argument is currently unused, but might be needed again someday. - */ -static void -fix_expr_references(Plan *plan, Node *node) -{ - /* This tree walk requires no special setup, so away we go... */ - fix_expr_references_walker(node, NULL); -} - -static bool -fix_expr_references_walker(Node *node, void *context) -{ - if (node == NULL) - return false; + /* + * Since we update opcode info in-place, this part could possibly + * scribble on the planner's input data structures, but it's OK. + */ if (IsA(node, OpExpr)) set_opfuncid((OpExpr *) node); else if (IsA(node, DistinctExpr)) set_opfuncid((OpExpr *) node); /* rely on struct equivalence */ - else if (IsA(node, ScalarArrayOpExpr)) - set_sa_opfuncid((ScalarArrayOpExpr *) node); else if (IsA(node, NullIfExpr)) set_opfuncid((OpExpr *) node); /* rely on struct equivalence */ - else if (IsA(node, SubPlan)) - { - SubPlan *sp = (SubPlan *) node; - - sp->plan = set_plan_references(sp->plan, sp->rtable); - } - return expression_tree_walker(node, fix_expr_references_walker, context); + else if (IsA(node, ScalarArrayOpExpr)) + set_sa_opfuncid((ScalarArrayOpExpr *) node); + return expression_tree_mutator(node, fix_scan_expr_mutator, + (void *) context); } /* * set_join_references - * Modifies the target list and quals of a join node to reference its + * Modify the target list and quals of a join node to reference its * subplans, by setting the varnos to OUTER or INNER and setting attno * values to the result domain number of either the corresponding outer - * or inner join tuple item. + * or inner join tuple item. Also perform opcode lookup for these + * expressions. * * In the case of a nestloop with inner indexscan, we will also need to * apply the same transformation to any outer vars appearing in the * quals of the child indexscan. set_inner_join_references does that. - * - * 'join' is a join plan node */ static void -set_join_references(Join *join) +set_join_references(Join *join, int rtoffset) { Plan *outer_plan = join->plan.lefttree; Plan *inner_plan = join->plan.righttree; @@ -728,43 +646,47 @@ set_join_references(Join *join) inner_itlist = build_tlist_index(inner_plan->targetlist); /* All join plans have tlist, qual, and joinqual */ - join->plan.targetlist = join_references(join->plan.targetlist, - outer_itlist, - inner_itlist, - (Index) 0); - join->plan.qual = join_references(join->plan.qual, - outer_itlist, - inner_itlist, - (Index) 0); - join->joinqual = join_references(join->joinqual, - outer_itlist, - inner_itlist, - (Index) 0); + join->plan.targetlist = fix_join_expr(join->plan.targetlist, + outer_itlist, + inner_itlist, + (Index) 0, + rtoffset); + join->plan.qual = fix_join_expr(join->plan.qual, + outer_itlist, + inner_itlist, + (Index) 0, + rtoffset); + join->joinqual = fix_join_expr(join->joinqual, + outer_itlist, + inner_itlist, + (Index) 0, + rtoffset); /* Now do join-type-specific stuff */ if (IsA(join, NestLoop)) { /* This processing is split out to handle possible recursion */ - set_inner_join_references(inner_plan, - outer_itlist); + set_inner_join_references(inner_plan, outer_itlist); } else if (IsA(join, MergeJoin)) { MergeJoin *mj = (MergeJoin *) join; - mj->mergeclauses = join_references(mj->mergeclauses, - outer_itlist, - inner_itlist, - (Index) 0); + mj->mergeclauses = fix_join_expr(mj->mergeclauses, + outer_itlist, + inner_itlist, + (Index) 0, + rtoffset); } else if (IsA(join, HashJoin)) { HashJoin *hj = (HashJoin *) join; - hj->hashclauses = join_references(hj->hashclauses, - outer_itlist, - inner_itlist, - (Index) 0); + hj->hashclauses = fix_join_expr(hj->hashclauses, + outer_itlist, + inner_itlist, + (Index) 0, + rtoffset); } pfree(outer_itlist); @@ -779,6 +701,10 @@ set_join_references(Join *join) * to the bottom BitmapIndexScan nodes; likewise, appendrel indexscans * require recursing through Append nodes. This is split out as a separate * function so that it can recurse. + * + * Note we do *not* apply any rtoffset for non-join Vars; this is because + * the quals will be processed again by fix_scan_expr when the set_plan_refs + * recursion reaches the inner indexscan, and so we'd have done it twice. */ static void set_inner_join_references(Plan *inner_plan, indexed_tlist *outer_itlist) @@ -800,14 +726,16 @@ set_inner_join_references(Plan *inner_plan, indexed_tlist *outer_itlist) Index innerrel = innerscan->scan.scanrelid; /* only refs to outer vars get changed in the inner qual */ - innerscan->indexqualorig = join_references(indexqualorig, - outer_itlist, - NULL, - innerrel); - innerscan->indexqual = join_references(innerscan->indexqual, - outer_itlist, - NULL, - innerrel); + innerscan->indexqualorig = fix_join_expr(indexqualorig, + outer_itlist, + NULL, + innerrel, + 0); + innerscan->indexqual = fix_join_expr(innerscan->indexqual, + outer_itlist, + NULL, + innerrel, + 0); /* * We must fix the inner qpqual too, if it has join clauses (this @@ -815,10 +743,11 @@ set_inner_join_references(Plan *inner_plan, indexed_tlist *outer_itlist) * may get rechecked as qpquals). */ if (NumRelids((Node *) inner_plan->qual) > 1) - inner_plan->qual = join_references(inner_plan->qual, - outer_itlist, - NULL, - innerrel); + inner_plan->qual = fix_join_expr(inner_plan->qual, + outer_itlist, + NULL, + innerrel, + 0); } } else if (IsA(inner_plan, BitmapIndexScan)) @@ -835,14 +764,16 @@ set_inner_join_references(Plan *inner_plan, indexed_tlist *outer_itlist) Index innerrel = innerscan->scan.scanrelid; /* only refs to outer vars get changed in the inner qual */ - innerscan->indexqualorig = join_references(indexqualorig, - outer_itlist, - NULL, - innerrel); - innerscan->indexqual = join_references(innerscan->indexqual, - outer_itlist, - NULL, - innerrel); + innerscan->indexqualorig = fix_join_expr(indexqualorig, + outer_itlist, + NULL, + innerrel, + 0); + innerscan->indexqual = fix_join_expr(innerscan->indexqual, + outer_itlist, + NULL, + innerrel, + 0); /* no need to fix inner qpqual */ Assert(inner_plan->qual == NIL); } @@ -862,10 +793,11 @@ set_inner_join_references(Plan *inner_plan, indexed_tlist *outer_itlist) /* only refs to outer vars get changed in the inner qual */ if (NumRelids((Node *) bitmapqualorig) > 1) - innerscan->bitmapqualorig = join_references(bitmapqualorig, - outer_itlist, - NULL, - innerrel); + innerscan->bitmapqualorig = fix_join_expr(bitmapqualorig, + outer_itlist, + NULL, + innerrel, + 0); /* * We must fix the inner qpqual too, if it has join clauses (this @@ -873,14 +805,14 @@ set_inner_join_references(Plan *inner_plan, indexed_tlist *outer_itlist) * get rechecked as qpquals). */ if (NumRelids((Node *) inner_plan->qual) > 1) - inner_plan->qual = join_references(inner_plan->qual, - outer_itlist, - NULL, - innerrel); + inner_plan->qual = fix_join_expr(inner_plan->qual, + outer_itlist, + NULL, + innerrel, + 0); /* Now recurse */ - set_inner_join_references(inner_plan->lefttree, - outer_itlist); + set_inner_join_references(inner_plan->lefttree, outer_itlist); } else if (IsA(inner_plan, BitmapAnd)) { @@ -890,8 +822,7 @@ set_inner_join_references(Plan *inner_plan, indexed_tlist *outer_itlist) foreach(l, innerscan->bitmapplans) { - set_inner_join_references((Plan *) lfirst(l), - outer_itlist); + set_inner_join_references((Plan *) lfirst(l), outer_itlist); } } else if (IsA(inner_plan, BitmapOr)) @@ -902,10 +833,20 @@ set_inner_join_references(Plan *inner_plan, indexed_tlist *outer_itlist) foreach(l, innerscan->bitmapplans) { - set_inner_join_references((Plan *) lfirst(l), - outer_itlist); + set_inner_join_references((Plan *) lfirst(l), outer_itlist); } } + else if (IsA(inner_plan, TidScan)) + { + TidScan *innerscan = (TidScan *) inner_plan; + Index innerrel = innerscan->scan.scanrelid; + + innerscan->tidquals = fix_join_expr(innerscan->tidquals, + outer_itlist, + NULL, + innerrel, + 0); + } else if (IsA(inner_plan, Append)) { /* @@ -917,8 +858,7 @@ set_inner_join_references(Plan *inner_plan, indexed_tlist *outer_itlist) foreach(l, appendplan->appendplans) { - set_inner_join_references((Plan *) lfirst(l), - outer_itlist); + set_inner_join_references((Plan *) lfirst(l), outer_itlist); } } else if (IsA(inner_plan, Result)) @@ -929,22 +869,13 @@ set_inner_join_references(Plan *inner_plan, indexed_tlist *outer_itlist) if (result->plan.lefttree) set_inner_join_references(result->plan.lefttree, outer_itlist); } - else if (IsA(inner_plan, TidScan)) - { - TidScan *innerscan = (TidScan *) inner_plan; - Index innerrel = innerscan->scan.scanrelid; - - innerscan->tidquals = join_references(innerscan->tidquals, - outer_itlist, - NULL, - innerrel); - } } /* - * set_uppernode_references + * set_upper_references * Update the targetlist and quals of an upper-level plan node * to refer to the tuples returned by its lefttree subplan. + * Also perform opcode lookup for these expressions. * * This is used for single-input plan types like Agg, Group, Result. * @@ -958,7 +889,7 @@ set_inner_join_references(Plan *inner_plan, indexed_tlist *outer_itlist) * the expression. */ static void -set_uppernode_references(Plan *plan, Index subvarno) +set_upper_references(Plan *plan, Index subvarno, int rtoffset) { Plan *subplan = plan->lefttree; indexed_tlist *subplan_itlist; @@ -976,9 +907,10 @@ set_uppernode_references(Plan *plan, Index subvarno) TargetEntry *tle = (TargetEntry *) lfirst(l); Node *newexpr; - newexpr = replace_vars_with_subplan_refs((Node *) tle->expr, - subplan_itlist, - subvarno); + newexpr = fix_upper_expr((Node *) tle->expr, + subplan_itlist, + subvarno, + rtoffset); tle = flatCopyTargetEntry(tle); tle->expr = (Expr *) newexpr; output_targetlist = lappend(output_targetlist, tle); @@ -986,9 +918,10 @@ set_uppernode_references(Plan *plan, Index subvarno) plan->targetlist = output_targetlist; plan->qual = (List *) - replace_vars_with_subplan_refs((Node *) plan->qual, - subplan_itlist, - subvarno); + fix_upper_expr((Node *) plan->qual, + subplan_itlist, + subvarno, + rtoffset); pfree(subplan_itlist); } @@ -1096,10 +1029,12 @@ build_tlist_index_other_vars(List *tlist, Index ignore_rel) * * If a match is found, return a copy of the given Var with suitably * modified varno/varattno (to wit, newvarno and the resno of the TLE entry). + * Also ensure that varnoold is incremented by rtoffset. * If no match, return NULL. */ static Var * -search_indexed_tlist_for_var(Var *var, indexed_tlist *itlist, Index newvarno) +search_indexed_tlist_for_var(Var *var, indexed_tlist *itlist, + Index newvarno, int rtoffset) { Index varno = var->varno; AttrNumber varattno = var->varattno; @@ -1117,6 +1052,8 @@ search_indexed_tlist_for_var(Var *var, indexed_tlist *itlist, Index newvarno) newvar->varno = newvarno; newvar->varattno = vinfo->resno; + if (newvar->varnoold > 0) + newvar->varnoold += rtoffset; return newvar; } vinfo++; @@ -1157,22 +1094,23 @@ search_indexed_tlist_for_non_var(Node *node, } /* - * join_references - * Creates a new set of targetlist entries or join qual clauses by + * fix_join_expr + * Create a new set of targetlist entries or join qual clauses by * changing the varno/varattno values of variables in the clauses * to reference target list values from the outer and inner join - * relation target lists. + * relation target lists. Also perform opcode lookup. * * This is used in two different scenarios: a normal join clause, where * all the Vars in the clause *must* be replaced by OUTER or INNER references; * and an indexscan being used on the inner side of a nestloop join. * In the latter case we want to replace the outer-relation Vars by OUTER - * references, but not touch the Vars of the inner relation. (We also - * implement RETURNING clause fixup using this second scenario.) + * references, while Vars of the inner relation should be adjusted by rtoffset. + * (We also implement RETURNING clause fixup using this second scenario.) * * For a normal join, acceptable_rel should be zero so that any failure to * match a Var will be reported as an error. For the indexscan case, - * pass inner_itlist = NULL and acceptable_rel = the ID of the inner relation. + * pass inner_itlist = NULL and acceptable_rel = the (not-offseted-yet) ID + * of the inner relation. * * 'clauses' is the targetlist or list of join clauses * 'outer_itlist' is the indexed target list of the outer join relation @@ -1180,27 +1118,29 @@ search_indexed_tlist_for_non_var(Node *node, * or NULL * 'acceptable_rel' is either zero or the rangetable index of a relation * whose Vars may appear in the clause without provoking an error. + * 'rtoffset' is what to add to varno for Vars of acceptable_rel. * * Returns the new expression tree. The original clause structure is * not modified. */ static List * -join_references(List *clauses, - indexed_tlist *outer_itlist, - indexed_tlist *inner_itlist, - Index acceptable_rel) +fix_join_expr(List *clauses, + indexed_tlist *outer_itlist, + indexed_tlist *inner_itlist, + Index acceptable_rel, + int rtoffset) { - join_references_context context; + fix_join_expr_context context; context.outer_itlist = outer_itlist; context.inner_itlist = inner_itlist; context.acceptable_rel = acceptable_rel; - return (List *) join_references_mutator((Node *) clauses, &context); + context.rtoffset = rtoffset; + return (List *) fix_join_expr_mutator((Node *) clauses, &context); } static Node * -join_references_mutator(Node *node, - join_references_context *context) +fix_join_expr_mutator(Node *node, fix_join_expr_context *context) { Var *newvar; @@ -1213,21 +1153,28 @@ join_references_mutator(Node *node, /* First look for the var in the input tlists */ newvar = search_indexed_tlist_for_var(var, context->outer_itlist, - OUTER); + OUTER, + context->rtoffset); if (newvar) return (Node *) newvar; if (context->inner_itlist) { newvar = search_indexed_tlist_for_var(var, context->inner_itlist, - INNER); + INNER, + context->rtoffset); if (newvar) return (Node *) newvar; } - /* Return the Var unmodified, if it's for acceptable_rel */ + /* If it's for acceptable_rel, adjust and return it */ if (var->varno == context->acceptable_rel) - return (Node *) copyObject(var); + { + var = (Var *) copyObject(var); + var->varno += context->rtoffset; + var->varnoold += context->rtoffset; + return (Node *) var; + } /* No referent found for Var */ elog(ERROR, "variable not found in subplan target lists"); @@ -1249,16 +1196,30 @@ join_references_mutator(Node *node, if (newvar) return (Node *) newvar; } + /* + * Since we update opcode info in-place, this part could possibly + * scribble on the planner's input data structures, but it's OK. + */ + if (IsA(node, OpExpr)) + set_opfuncid((OpExpr *) node); + else if (IsA(node, DistinctExpr)) + set_opfuncid((OpExpr *) node); /* rely on struct equivalence */ + else if (IsA(node, NullIfExpr)) + set_opfuncid((OpExpr *) node); /* rely on struct equivalence */ + else if (IsA(node, ScalarArrayOpExpr)) + set_sa_opfuncid((ScalarArrayOpExpr *) node); return expression_tree_mutator(node, - join_references_mutator, + fix_join_expr_mutator, (void *) context); } /* - * replace_vars_with_subplan_refs - * This routine modifies an expression tree so that all Var nodes - * reference target nodes of a subplan. It is used to fix up - * target and qual expressions of non-join upper-level plan nodes. + * fix_upper_expr + * Modifies an expression tree so that all Var nodes reference outputs + * of a subplan. Also performs opcode lookup. + * + * This is used to fix up target and qual expressions of non-join upper-level + * plan nodes. * * An error is raised if no matching var can be found in the subplan tlist * --- so this routine should only be applied to nodes whose subplans' @@ -1273,26 +1234,28 @@ join_references_mutator(Node *node, * 'node': the tree to be fixed (a target item or qual) * 'subplan_itlist': indexed target list for subplan * 'subvarno': varno to be assigned to all Vars + * 'rtoffset': how much to increment varnoold by * * The resulting tree is a copy of the original in which all Var nodes have * varno = subvarno, varattno = resno of corresponding subplan target. * The original tree is not modified. */ static Node * -replace_vars_with_subplan_refs(Node *node, - indexed_tlist *subplan_itlist, - Index subvarno) +fix_upper_expr(Node *node, + indexed_tlist *subplan_itlist, + Index subvarno, + int rtoffset) { - replace_vars_with_subplan_refs_context context; + fix_upper_expr_context context; context.subplan_itlist = subplan_itlist; context.subvarno = subvarno; - return replace_vars_with_subplan_refs_mutator(node, &context); + context.rtoffset = rtoffset; + return fix_upper_expr_mutator(node, &context); } static Node * -replace_vars_with_subplan_refs_mutator(Node *node, - replace_vars_with_subplan_refs_context *context) +fix_upper_expr_mutator(Node *node, fix_upper_expr_context *context) { Var *newvar; @@ -1304,7 +1267,8 @@ replace_vars_with_subplan_refs_mutator(Node *node, newvar = search_indexed_tlist_for_var(var, context->subplan_itlist, - context->subvarno); + context->subvarno, + context->rtoffset); if (!newvar) elog(ERROR, "variable not found in subplan target list"); return (Node *) newvar; @@ -1318,8 +1282,20 @@ replace_vars_with_subplan_refs_mutator(Node *node, if (newvar) return (Node *) newvar; } + /* + * Since we update opcode info in-place, this part could possibly + * scribble on the planner's input data structures, but it's OK. + */ + if (IsA(node, OpExpr)) + set_opfuncid((OpExpr *) node); + else if (IsA(node, DistinctExpr)) + set_opfuncid((OpExpr *) node); /* rely on struct equivalence */ + else if (IsA(node, NullIfExpr)) + set_opfuncid((OpExpr *) node); /* rely on struct equivalence */ + else if (IsA(node, ScalarArrayOpExpr)) + set_sa_opfuncid((ScalarArrayOpExpr *) node); return expression_tree_mutator(node, - replace_vars_with_subplan_refs_mutator, + fix_upper_expr_mutator, (void *) context); } @@ -1335,12 +1311,15 @@ replace_vars_with_subplan_refs_mutator(Node *node, * adjusted RETURNING list, result-table Vars will still have their * original varno, but Vars for other rels will have varno OUTER. * - * We also must apply fix_expr_references to the list. + * We also must perform opcode lookup. * * 'rlist': the RETURNING targetlist to be fixed * 'topplan': the top Plan node for the query (not yet passed through * set_plan_references) - * 'resultRelation': RT index of the query's result relation + * 'resultRelation': RT index of the associated result relation + * + * Note: we assume that result relations will have rtoffset zero, that is, + * they are not coming from a subplan. */ List * set_returning_clause_references(List *rlist, @@ -1350,20 +1329,19 @@ set_returning_clause_references(List *rlist, indexed_tlist *itlist; /* - * We can perform the desired Var fixup by abusing the join_references + * We can perform the desired Var fixup by abusing the fix_join_expr * machinery that normally handles inner indexscan fixup. We search the * top plan's targetlist for Vars of non-result relations, and use - * join_references to convert RETURNING Vars into references to those + * fix_join_expr to convert RETURNING Vars into references to those * tlist entries, while leaving result-rel Vars as-is. */ itlist = build_tlist_index_other_vars(topplan->targetlist, resultRelation); - rlist = join_references(rlist, - itlist, - NULL, - resultRelation); - - fix_expr_references(topplan, (Node *) rlist); + rlist = fix_join_expr(rlist, + itlist, + NULL, + resultRelation, + 0); pfree(itlist); @@ -1399,10 +1377,10 @@ fix_opfuncids_walker(Node *node, void *context) set_opfuncid((OpExpr *) node); else if (IsA(node, DistinctExpr)) set_opfuncid((OpExpr *) node); /* rely on struct equivalence */ - else if (IsA(node, ScalarArrayOpExpr)) - set_sa_opfuncid((ScalarArrayOpExpr *) node); else if (IsA(node, NullIfExpr)) set_opfuncid((OpExpr *) node); /* rely on struct equivalence */ + else if (IsA(node, ScalarArrayOpExpr)) + set_sa_opfuncid((ScalarArrayOpExpr *) node); return expression_tree_walker(node, fix_opfuncids_walker, context); } diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c index b62aeb853d85e0e4e460c1e8cb696ff3afe6a1cc..d19e9d298c01cbd632811cdb6d6f08e4a7ada497 100644 --- a/src/backend/optimizer/plan/subselect.c +++ b/src/backend/optimizer/plan/subselect.c @@ -7,7 +7,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/optimizer/plan/subselect.c,v 1.120 2007/02/19 07:03:30 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/plan/subselect.c,v 1.121 2007/02/22 22:00:24 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -46,6 +46,7 @@ typedef struct process_sublinks_context typedef struct finalize_primnode_context { + PlannerInfo *root; Bitmapset *paramids; /* Set of PARAM_EXEC paramids found */ Bitmapset *outer_params; /* Set of accessible outer paramids */ } finalize_primnode_context; @@ -57,12 +58,13 @@ static Node *convert_testexpr(PlannerInfo *root, List **righthandIds); static Node *convert_testexpr_mutator(Node *node, convert_testexpr_context *context); -static bool subplan_is_hashable(SubLink *slink, SubPlan *node); +static bool subplan_is_hashable(SubLink *slink, SubPlan *node, Plan *plan); static bool hash_ok_operator(OpExpr *expr); static Node *replace_correlation_vars_mutator(Node *node, PlannerInfo *root); static Node *process_sublinks_mutator(Node *node, process_sublinks_context *context); -static Bitmapset *finalize_plan(Plan *plan, List *rtable, +static Bitmapset *finalize_plan(PlannerInfo *root, + Plan *plan, Bitmapset *outer_params, Bitmapset *valid_params); static bool finalize_primnode(Node *node, finalize_primnode_context *context); @@ -203,6 +205,24 @@ generate_new_param(PlannerInfo *root, Oid paramtype, int32 paramtypmod) return retval; } +/* + * Get the datatype of the first column of the plan's output. + * + * This is a hack to support exprType(), which doesn't have any way to get + * at the plan associated with a SubPlan node. We really only need the value + * for EXPR_SUBLINK and ARRAY_SUBLINK subplans, but for consistency we set + * it always. + */ +static Oid +get_first_col_type(Plan *plan) +{ + TargetEntry *tent = (TargetEntry *) linitial(plan->targetlist); + + Assert(IsA(tent, TargetEntry)); + Assert(!tent->resjunk); + return exprType((Node *) tent->expr); +} + /* * Convert a SubLink (as created by the parser) into a SubPlan. * @@ -219,10 +239,11 @@ generate_new_param(PlannerInfo *root, Oid paramtype, int32 paramtypmod) static Node * make_subplan(PlannerInfo *root, SubLink *slink, Node *testexpr, bool isTopQual) { - SubPlan *node = makeNode(SubPlan); Query *subquery = (Query *) (slink->subselect); double tuple_fraction; + SubPlan *node; Plan *plan; + PlannerInfo *subroot; Bitmapset *tmpset; int paramid; Node *result; @@ -266,22 +287,19 @@ make_subplan(PlannerInfo *root, SubLink *slink, Node *testexpr, bool isTopQual) /* * Generate the plan for the subquery. */ - node->plan = plan = subquery_planner(root->glob, subquery, - root->query_level + 1, - tuple_fraction, - NULL); - - /* Assign quasi-unique ID to this SubPlan */ - node->plan_id = root->glob->next_plan_id++; - - node->rtable = subquery->rtable; + plan = subquery_planner(root->glob, subquery, + root->query_level + 1, + tuple_fraction, + &subroot); /* - * Initialize other fields of the SubPlan node. + * Initialize the SubPlan node. Note plan_id isn't set yet. */ + node = makeNode(SubPlan); node->subLinkType = slink->subLinkType; node->testexpr = NULL; node->paramIds = NIL; + node->firstColType = get_first_col_type(plan); node->useHashTable = false; /* At top level of a qual, can treat UNKNOWN the same as FALSE */ node->unknownEqFalse = isTopQual; @@ -384,7 +402,7 @@ make_subplan(PlannerInfo *root, SubLink *slink, Node *testexpr, bool isTopQual) * tuple. But if it's an IN (= ANY) test, we might be able to use a * hashtable to avoid comparing all the tuples. */ - if (subplan_is_hashable(slink, node)) + if (subplan_is_hashable(slink, node, plan)) node->useHashTable = true; /* @@ -411,7 +429,7 @@ make_subplan(PlannerInfo *root, SubLink *slink, Node *testexpr, bool isTopQual) break; } if (use_material) - node->plan = plan = materialize_finished_plan(plan); + plan = materialize_finished_plan(plan); } /* @@ -435,6 +453,15 @@ make_subplan(PlannerInfo *root, SubLink *slink, Node *testexpr, bool isTopQual) result = (Node *) node; } + /* + * Add the subplan and its rtable to the global lists. + */ + root->glob->subplans = lappend(root->glob->subplans, + plan); + root->glob->subrtables = lappend(root->glob->subrtables, + subroot->parse->rtable); + node->plan_id = list_length(root->glob->subplans); + return result; } @@ -539,7 +566,7 @@ convert_testexpr_mutator(Node *node, * on its plan and parParam fields, however. */ static bool -subplan_is_hashable(SubLink *slink, SubPlan *node) +subplan_is_hashable(SubLink *slink, SubPlan *node, Plan *plan) { double subquery_size; ListCell *l; @@ -574,8 +601,8 @@ subplan_is_hashable(SubLink *slink, SubPlan *node) * actually be stored as MinimalTuples; this provides some fudge factor * for hashtable overhead.) */ - subquery_size = node->plan->plan_rows * - (MAXALIGN(node->plan->plan_width) + MAXALIGN(sizeof(HeapTupleHeaderData))); + subquery_size = plan->plan_rows * + (MAXALIGN(plan->plan_width) + MAXALIGN(sizeof(HeapTupleHeaderData))); if (subquery_size > work_mem * 1024L) return false; @@ -964,7 +991,7 @@ SS_finalize_plan(PlannerInfo *root, Plan *plan) /* * Now recurse through plan tree. */ - (void) finalize_plan(plan, root->parse->rtable, outer_params, valid_params); + (void) finalize_plan(root, plan, outer_params, valid_params); bms_free(outer_params); bms_free(valid_params); @@ -988,16 +1015,16 @@ SS_finalize_plan(PlannerInfo *root, Plan *plan) initplan_cost = 0; foreach(l, plan->initPlan) { - SubPlan *initplan = (SubPlan *) lfirst(l); + SubPlan *initsubplan = (SubPlan *) lfirst(l); + Plan *initplan = planner_subplan_get_plan(root, initsubplan); ListCell *l2; - initExtParam = bms_add_members(initExtParam, - initplan->plan->extParam); - foreach(l2, initplan->setParam) + initExtParam = bms_add_members(initExtParam, initplan->extParam); + foreach(l2, initsubplan->setParam) { initSetParam = bms_add_member(initSetParam, lfirst_int(l2)); } - initplan_cost += initplan->plan->total_cost; + initplan_cost += initplan->total_cost; } /* allParam must include all these params */ plan->allParam = bms_add_members(plan->allParam, initExtParam); @@ -1019,7 +1046,7 @@ SS_finalize_plan(PlannerInfo *root, Plan *plan) * This is just an internal notational convenience. */ static Bitmapset * -finalize_plan(Plan *plan, List *rtable, +finalize_plan(PlannerInfo *root, Plan *plan, Bitmapset *outer_params, Bitmapset *valid_params) { finalize_primnode_context context; @@ -1027,6 +1054,7 @@ finalize_plan(Plan *plan, List *rtable, if (plan == NULL) return NULL; + context.root = root; context.paramids = NULL; /* initialize set to empty */ context.outer_params = outer_params; @@ -1110,8 +1138,8 @@ finalize_plan(Plan *plan, List *rtable, { context.paramids = bms_add_members(context.paramids, - finalize_plan((Plan *) lfirst(l), - rtable, + finalize_plan(root, + (Plan *) lfirst(l), outer_params, valid_params)); } @@ -1126,8 +1154,8 @@ finalize_plan(Plan *plan, List *rtable, { context.paramids = bms_add_members(context.paramids, - finalize_plan((Plan *) lfirst(l), - rtable, + finalize_plan(root, + (Plan *) lfirst(l), outer_params, valid_params)); } @@ -1142,8 +1170,8 @@ finalize_plan(Plan *plan, List *rtable, { context.paramids = bms_add_members(context.paramids, - finalize_plan((Plan *) lfirst(l), - rtable, + finalize_plan(root, + (Plan *) lfirst(l), outer_params, valid_params)); } @@ -1193,14 +1221,14 @@ finalize_plan(Plan *plan, List *rtable, /* Process left and right child plans, if any */ context.paramids = bms_add_members(context.paramids, - finalize_plan(plan->lefttree, - rtable, + finalize_plan(root, + plan->lefttree, outer_params, valid_params)); context.paramids = bms_add_members(context.paramids, - finalize_plan(plan->righttree, - rtable, + finalize_plan(root, + plan->righttree, outer_params, valid_params)); @@ -1252,10 +1280,11 @@ finalize_primnode(Node *node, finalize_primnode_context *context) if (is_subplan(node)) { SubPlan *subplan = (SubPlan *) node; + Plan *plan = planner_subplan_get_plan(context->root, subplan); /* Add outer-level params needed by the subplan to paramids */ context->paramids = bms_join(context->paramids, - bms_intersect(subplan->plan->extParam, + bms_intersect(plan->extParam, context->outer_params)); /* fall through to recurse into subplan args */ } @@ -1299,16 +1328,21 @@ SS_make_initplan_from_plan(PlannerInfo *root, Plan *plan, root->query_level--; root->init_plans = saved_init_plans; + /* + * Add the subplan and its rtable to the global lists. + */ + root->glob->subplans = lappend(root->glob->subplans, + plan); + root->glob->subrtables = lappend(root->glob->subrtables, + root->parse->rtable); + /* * Create a SubPlan node and add it to the outer list of InitPlans. */ node = makeNode(SubPlan); node->subLinkType = EXPR_SUBLINK; - node->plan = plan; - /* Assign quasi-unique ID to this SubPlan */ - node->plan_id = root->glob->next_plan_id++; - - node->rtable = root->parse->rtable; + node->firstColType = get_first_col_type(plan); + node->plan_id = list_length(root->glob->subplans); root->init_plans = lappend(root->init_plans, node); diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c index 2f06e1bf5322dfe3a03c14d310c9dbc03d964590..f09ddb1d23d1111205017deea44617f66fef7be0 100644 --- a/src/backend/optimizer/prep/prepunion.c +++ b/src/backend/optimizer/prep/prepunion.c @@ -22,7 +22,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/optimizer/prep/prepunion.c,v 1.138 2007/02/19 07:03:30 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/prep/prepunion.c,v 1.139 2007/02/22 22:00:24 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -169,6 +169,7 @@ recurse_set_operations(Node *setOp, PlannerInfo *root, RangeTblRef *rtr = (RangeTblRef *) setOp; RangeTblEntry *rte = rt_fetch(rtr->rtindex, root->parse->rtable); Query *subquery = rte->subquery; + PlannerInfo *subroot; Plan *subplan, *plan; @@ -180,7 +181,7 @@ recurse_set_operations(Node *setOp, PlannerInfo *root, subplan = subquery_planner(root->glob, subquery, root->query_level + 1, tuple_fraction, - NULL); + &subroot); /* * Add a SubqueryScan with the caller-requested targetlist @@ -193,7 +194,8 @@ recurse_set_operations(Node *setOp, PlannerInfo *root, refnames_tlist), NIL, rtr->rtindex, - subplan); + subplan, + subroot->parse->rtable); /* * We don't bother to determine the subquery's output ordering since @@ -223,7 +225,7 @@ recurse_set_operations(Node *setOp, PlannerInfo *root, * output columns. * * XXX you don't really want to know about this: setrefs.c will apply - * replace_vars_with_subplan_refs() to the Result node's tlist. This + * fix_upper_expr() to the Result node's tlist. This * would fail if the Vars generated by generate_setop_tlist() were not * exactly equal() to the corresponding tlist entries of the subplan. * However, since the subplan was generated by generate_union_plan() @@ -235,7 +237,8 @@ recurse_set_operations(Node *setOp, PlannerInfo *root, !tlist_same_datatypes(plan->targetlist, colTypes, junkOK)) { plan = (Plan *) - make_result(generate_setop_tlist(colTypes, flag, + make_result(root, + generate_setop_tlist(colTypes, flag, 0, false, plan->targetlist, @@ -1216,28 +1219,6 @@ adjust_appendrel_attrs_mutator(Node *node, AppendRelInfo *context) Assert(!IsA(node, SubLink)); Assert(!IsA(node, Query)); - /* - * BUT: although we don't need to recurse into subplans, we do need to - * make sure that they are copied, not just referenced as - * expression_tree_mutator will do by default. Otherwise we'll have the - * same subplan node referenced from each arm of the finished APPEND plan, - * which will cause trouble in the executor. This is a kluge that should - * go away when we redesign querytrees. - */ - if (is_subplan(node)) - { - SubPlan *subplan; - - /* Copy the node and process subplan args */ - node = expression_tree_mutator(node, adjust_appendrel_attrs_mutator, - (void *) context); - /* Make sure we have separate copies of subplan and its rtable */ - subplan = (SubPlan *) node; - subplan->plan = copyObject(subplan->plan); - subplan->rtable = copyObject(subplan->rtable); - return node; - } - return expression_tree_mutator(node, adjust_appendrel_attrs_mutator, (void *) context); } diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c index f90d55001979b65c15f8fdb222473cc349e83a1e..852b0f533e75d4251e128bb5aae0a3eb3e05b631 100644 --- a/src/backend/optimizer/util/clauses.c +++ b/src/backend/optimizer/util/clauses.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.235 2007/02/19 07:03:30 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.236 2007/02/22 22:00:24 tgl Exp $ * * HISTORY * AUTHOR DATE MAJOR EVENT @@ -2989,7 +2989,7 @@ inline_function(Oid funcid, Oid result_type, List *args, */ if (contain_subplans(param)) goto fail; - cost_qual_eval(&eval_cost, list_make1(param)); + cost_qual_eval(&eval_cost, list_make1(param), NULL); if (eval_cost.startup + eval_cost.per_tuple > 10 * cpu_operator_cost) goto fail; diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c index 8cc6c81746f3db605c323198a344483d1c2c2e69..6dfd2f61149e71c2ef87ec965b64a1b850e04aea 100644 --- a/src/backend/optimizer/util/relnode.c +++ b/src/backend/optimizer/util/relnode.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/optimizer/util/relnode.c,v 1.85 2007/01/20 20:45:40 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/util/relnode.c,v 1.86 2007/02/22 22:00:25 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -82,6 +82,7 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptKind reloptkind) rel->pages = 0; rel->tuples = 0; rel->subplan = NULL; + rel->subrtable = NIL; rel->baserestrictinfo = NIL; rel->baserestrictcost.startup = 0; rel->baserestrictcost.per_tuple = 0; @@ -333,6 +334,7 @@ build_join_rel(PlannerInfo *root, joinrel->pages = 0; joinrel->tuples = 0; joinrel->subplan = NULL; + joinrel->subrtable = NIL; joinrel->baserestrictinfo = NIL; joinrel->baserestrictcost.startup = 0; joinrel->baserestrictcost.per_tuple = 0; diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c index 8f34748f9987372ab03f8e52116d3bf428fc0592..5791215a95f29589e5ab9375498d0a6e3f182c61 100644 --- a/src/backend/parser/parse_expr.c +++ b/src/backend/parser/parse_expr.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.211 2007/02/11 22:18:15 petere Exp $ + * $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.212 2007/02/22 22:00:25 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1769,12 +1769,7 @@ exprType(Node *expr) subplan->subLinkType == ARRAY_SUBLINK) { /* get the type of the subselect's first target column */ - TargetEntry *tent; - - tent = (TargetEntry *) linitial(subplan->plan->targetlist); - Assert(IsA(tent, TargetEntry)); - Assert(!tent->resjunk); - type = exprType((Node *) tent->expr); + type = subplan->firstColType; if (subplan->subLinkType == ARRAY_SUBLINK) { type = get_array_type(type); @@ -1782,7 +1777,7 @@ exprType(Node *expr) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("could not find array type for data type %s", - format_type_be(exprType((Node *) tent->expr))))); + format_type_be(subplan->firstColType)))); } } else diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c index c6b8e8481c498d822de7700ca293713910021716..f25a8d8f70de62575cec804b5bedd90b5efdbb6b 100644 --- a/src/backend/utils/adt/selfuncs.c +++ b/src/backend/utils/adt/selfuncs.c @@ -15,7 +15,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/adt/selfuncs.c,v 1.226 2007/02/19 07:03:31 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/utils/adt/selfuncs.c,v 1.227 2007/02/22 22:00:25 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -4964,7 +4964,7 @@ genericcostestimate(PlannerInfo *root, * more expensive than it's worth, though, considering all the other * inaccuracies here ... */ - cost_qual_eval(&index_qual_cost, indexQuals); + cost_qual_eval(&index_qual_cost, indexQuals, root); qual_op_cost = cpu_operator_cost * list_length(indexQuals); qual_arg_cost = index_qual_cost.startup + index_qual_cost.per_tuple - qual_op_cost; diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h index 38260b5ecd6c211dcd9b18ed590f688b0d04e1ee..7dfcabf5ac28227979700e7794a3baa71587a6bb 100644 --- a/src/include/executor/executor.h +++ b/src/include/executor/executor.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/executor/executor.h,v 1.137 2007/02/20 17:32:17 tgl Exp $ + * $PostgreSQL: pgsql/src/include/executor/executor.h,v 1.138 2007/02/22 22:00:25 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -130,7 +130,6 @@ extern TupleTableSlot *ExecutorRun(QueryDesc *queryDesc, ScanDirection direction, long count); extern void ExecutorEnd(QueryDesc *queryDesc); extern void ExecutorRewind(QueryDesc *queryDesc); -extern void ExecCheckRTPerms(List *rangeTable); extern void ExecEndPlan(PlanState *planstate, EState *estate); extern bool ExecContextForcesOids(PlanState *planstate, bool *hasoids); extern void ExecConstraints(ResultRelInfo *resultRelInfo, diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index b576f4610e3e75e7b7e84de5bb6b52b676db18ee..d0d47a8fd2b1ffce3a43753d14165fcff5d0002f 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/nodes/execnodes.h,v 1.168 2007/02/20 17:32:17 tgl Exp $ + * $PostgreSQL: pgsql/src/include/nodes/execnodes.h,v 1.169 2007/02/22 22:00:25 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -302,7 +302,7 @@ typedef struct EState ScanDirection es_direction; /* current scan direction */ Snapshot es_snapshot; /* time qual to use */ Snapshot es_crosscheck_snapshot; /* crosscheck time qual for RI */ - List *es_range_table; /* List of RangeTableEntrys */ + List *es_range_table; /* List of RangeTblEntry */ /* Info about target table for insert/update/delete queries: */ ResultRelInfo *es_result_relations; /* array of ResultRelInfos */ diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h index 537981462b23e7b51585f63ab97e9116e8c37106..5a7445f69c0522db705059fdca13beade304492a 100644 --- a/src/include/nodes/plannodes.h +++ b/src/include/nodes/plannodes.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/nodes/plannodes.h,v 1.91 2007/02/20 17:32:17 tgl Exp $ + * $PostgreSQL: pgsql/src/include/nodes/plannodes.h,v 1.92 2007/02/22 22:00:25 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -48,6 +48,8 @@ typedef struct PlannedStmt IntoClause *into; /* target for SELECT INTO / CREATE TABLE AS */ + List *subplans; /* Plan trees for SubPlan expressions */ + /* * If the query has a returningList then the planner will store a list of * processed targetlists (one per result relation) here. We must have a @@ -65,6 +67,10 @@ typedef struct PlannedStmt int nParamExec; /* number of PARAM_EXEC Params used */ } PlannedStmt; +/* macro for fetching the Plan associated with a SubPlan node */ +#define exec_subplan_get_plan(plannedstmt, subplan) \ + ((Plan *) list_nth((plannedstmt)->subplans, (subplan)->plan_id - 1)) + /* ---------------- * Plan node @@ -313,12 +319,17 @@ typedef struct TidScan * the generic lefttree field as you might expect. This is because we do * not want plan-tree-traversal routines to recurse into the subplan without * knowing that they are changing Query contexts. + * + * Note: subrtable is used just to carry the subquery rangetable from + * createplan.c to setrefs.c; it should always be NIL by the time the + * executor sees the plan. * ---------------- */ typedef struct SubqueryScan { Scan scan; Plan *subplan; + List *subrtable; /* temporary workspace for planner */ } SubqueryScan; /* ---------------- diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h index 185673f729a85f90750eff29f7e30ef2c720181b..7efb6ec77cfaa970fa1062b1d976d6ed97e85bac 100644 --- a/src/include/nodes/primnodes.h +++ b/src/include/nodes/primnodes.h @@ -10,7 +10,7 @@ * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/nodes/primnodes.h,v 1.126 2007/02/20 17:32:17 tgl Exp $ + * $PostgreSQL: pgsql/src/include/nodes/primnodes.h,v 1.127 2007/02/22 22:00:25 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -428,8 +428,10 @@ typedef struct SubLink * SubPlan - executable expression node for a subplan (sub-SELECT) * * The planner replaces SubLink nodes in expression trees with SubPlan - * nodes after it has finished planning the subquery. SubPlan contains - * a sub-plantree and rtable instead of a sub-Query. + * nodes after it has finished planning the subquery. SubPlan references + * a sub-plantree stored in the subplans list of the toplevel PlannedStmt. + * (We avoid a direct link to make it easier to copy expression trees + * without causing multiple processing of the subplan.) * * In an ordinary subplan, testexpr points to an executable expression * (OpExpr, an AND/OR tree of OpExprs, or RowCompareExpr) for the combining @@ -463,12 +465,10 @@ typedef struct SubPlan /* The combining operators, transformed to an executable expression: */ Node *testexpr; /* OpExpr or RowCompareExpr expression tree */ List *paramIds; /* IDs of Params embedded in the above */ - /* The subselect, transformed to a Plan: */ - struct Plan *plan; /* subselect plan itself */ - int plan_id; /* kluge because we haven't equal-funcs for - * plan nodes... we compare this instead of - * subselect plan */ - List *rtable; /* range table for subselect */ + /* Identification of the Plan tree to use: */ + int plan_id; /* Index (from 1) in PlannedStmt.subplans */ + /* Extra data saved for the convenience of exprType(): */ + Oid firstColType; /* Type of first column of subplan result */ /* Information about execution strategy: */ bool useHashTable; /* TRUE to store subselect output in a hash * table (implies we are doing "IN") */ diff --git a/src/include/nodes/print.h b/src/include/nodes/print.h index fdb0cb0a5aeacfe10492011af4f97375c02b8eaf..76ad5abe1f5df70cf2c5f0e5e37fd7e6d4649b06 100644 --- a/src/include/nodes/print.h +++ b/src/include/nodes/print.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/nodes/print.h,v 1.26 2007/01/05 22:19:56 momjian Exp $ + * $PostgreSQL: pgsql/src/include/nodes/print.h,v 1.27 2007/02/22 22:00:25 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -31,8 +31,5 @@ extern void print_expr(Node *expr, List *rtable); extern void print_pathkeys(List *pathkeys, List *rtable); extern void print_tl(List *tlist, List *rtable); extern void print_slot(TupleTableSlot *slot); -extern void print_plan_recursive(Plan *p, Query *parsetree, - int indentLevel, char *label); -extern void print_plan(Plan *p, Query *parsetree); #endif /* PRINT_H */ diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h index 59ec830f3ff6a23d1c065d7715a773d38bdb4037..19bcb51ea08c2bf8566f4033ca5a0a447ab52ff8 100644 --- a/src/include/nodes/relation.h +++ b/src/include/nodes/relation.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/nodes/relation.h,v 1.137 2007/02/20 17:32:17 tgl Exp $ + * $PostgreSQL: pgsql/src/include/nodes/relation.h,v 1.138 2007/02/22 22:00:26 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -64,9 +64,17 @@ typedef struct PlannerGlobal List *paramlist; /* to keep track of cross-level Params */ - int next_plan_id; /* hack for distinguishing SubPlans */ + List *subplans; /* Plans for SubPlan nodes */ + + List *subrtables; /* Rangetables for SubPlan nodes */ + + List *finalrtable; /* "flat" rangetable for executor */ } PlannerGlobal; +/* macro for fetching the Plan associated with a SubPlan node */ +#define planner_subplan_get_plan(root, subplan) \ + ((Plan *) list_nth((root)->glob->subplans, (subplan)->plan_id - 1)) + /*---------- * PlannerInfo @@ -228,6 +236,7 @@ typedef struct PlannerInfo * pages - number of disk pages in relation (zero if not a table) * tuples - number of tuples in relation (not considering restrictions) * subplan - plan for subquery (NULL if it's not a subquery) + * subrtable - rangetable for subquery (NIL if it's not a subquery) * * Note: for a subquery, tuples and subplan are not set immediately * upon creation of the RelOptInfo object; they are filled in when @@ -310,6 +319,7 @@ typedef struct RelOptInfo BlockNumber pages; double tuples; struct Plan *subplan; /* if subquery */ + List *subrtable; /* if subquery */ /* used by various scans and joins: */ List *baserestrictinfo; /* RestrictInfo structures (if base diff --git a/src/include/optimizer/cost.h b/src/include/optimizer/cost.h index fde65ed874a0d1cb6e4244d7478daac101d3e003..ef92d685eb4a433ef75bd77e5a7fbd9448bacef3 100644 --- a/src/include/optimizer/cost.h +++ b/src/include/optimizer/cost.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/optimizer/cost.h,v 1.84 2007/01/22 01:35:22 tgl Exp $ + * $PostgreSQL: pgsql/src/include/optimizer/cost.h,v 1.85 2007/02/22 22:00:26 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -88,8 +88,8 @@ extern void cost_group(Path *path, PlannerInfo *root, extern void cost_nestloop(NestPath *path, PlannerInfo *root); extern void cost_mergejoin(MergePath *path, PlannerInfo *root); extern void cost_hashjoin(HashPath *path, PlannerInfo *root); -extern void cost_qual_eval(QualCost *cost, List *quals); -extern void cost_qual_eval_node(QualCost *cost, Node *qual); +extern void cost_qual_eval(QualCost *cost, List *quals, PlannerInfo *root); +extern void cost_qual_eval_node(QualCost *cost, Node *qual, PlannerInfo *root); extern void set_baserel_size_estimates(PlannerInfo *root, RelOptInfo *rel); extern void set_joinrel_size_estimates(PlannerInfo *root, RelOptInfo *rel, RelOptInfo *outer_rel, diff --git a/src/include/optimizer/planmain.h b/src/include/optimizer/planmain.h index 6bf34e162a7c479f45cf951ee15d27a77b92425d..b5ad4b3c3b503a5ae93db14f8a613477963e1505 100644 --- a/src/include/optimizer/planmain.h +++ b/src/include/optimizer/planmain.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/optimizer/planmain.h,v 1.99 2007/01/22 01:35:22 tgl Exp $ + * $PostgreSQL: pgsql/src/include/optimizer/planmain.h,v 1.100 2007/02/22 22:00:26 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -36,7 +36,7 @@ extern Plan *optimize_minmax_aggregates(PlannerInfo *root, List *tlist, */ extern Plan *create_plan(PlannerInfo *root, Path *best_path); extern SubqueryScan *make_subqueryscan(List *qptlist, List *qpqual, - Index scanrelid, Plan *subplan); + Index scanrelid, Plan *subplan, List *subrtable); extern Append *make_append(List *appendplans, bool isTarget, List *tlist); extern Sort *make_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys); @@ -60,7 +60,8 @@ extern Limit *make_limit(Plan *lefttree, Node *limitOffset, Node *limitCount, int64 offset_est, int64 count_est); extern SetOp *make_setop(SetOpCmd cmd, Plan *lefttree, List *distinctList, AttrNumber flagColIdx); -extern Result *make_result(List *tlist, Node *resconstantqual, Plan *subplan); +extern Result *make_result(PlannerInfo *root, List *tlist, + Node *resconstantqual, Plan *subplan); extern bool is_projection_capable_plan(Plan *plan); /* @@ -91,7 +92,9 @@ extern RestrictInfo *build_implied_join_equality(Oid opno, /* * prototypes for plan/setrefs.c */ -extern Plan *set_plan_references(Plan *plan, List *rtable); +extern Plan *set_plan_references(PlannerGlobal *glob, + Plan *plan, + List *rtable); extern List *set_returning_clause_references(List *rlist, Plan *topplan, Index resultRelation);