Commit eab6b8b2 authored by Tom Lane's avatar Tom Lane

Turn the rangetable used by the executor into a flat list, and avoid storing

useless substructure for its RangeTblEntry nodes.  (I chose to keep using the
same struct node type and just zero out the link fields for unneeded info,
rather than making a separate ExecRangeTblEntry type --- it seemed too
fragile to have two different rangetable representations.)

Along the way, put subplans into a list in the toplevel PlannedStmt node,
and have SubPlan nodes refer to them by list index instead of direct pointers.
Vadim wanted to do that years ago, but I never understood what he was on about
until now.  It makes things a *whole* lot more robust, because we can stop
worrying about duplicate processing of subplans during expression tree
traversals.  That's been a constant source of bugs, and it's finally gone.

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