Commit bd3dadda authored by Tom Lane's avatar Tom Lane

Arrange to convert EXISTS subqueries that are equivalent to hashable IN

subqueries into the same thing you'd have gotten from IN (except always with
unknownEqFalse = true, so as to get the proper semantics for an EXISTS).
I believe this fixes the last case within CVS HEAD in which an EXISTS could
give worse performance than an equivalent IN subquery.

The tricky part of this is that if the upper query probes the EXISTS for only
a few rows, the hashing implementation can actually be worse than the default,
and therefore we need to make a cost-based decision about which way to use.
But at the time when the planner generates plans for subqueries, it doesn't
really know how many times the subquery will be executed.  The least invasive
solution seems to be to generate both plans and postpone the choice until
execution.  Therefore, in a query that has been optimized this way, EXPLAIN
will show two subplans for the EXISTS, of which only one will actually get
executed.

There is a lot more that could be done based on this infrastructure: in
particular it's interesting to consider switching to the hash plan if we start
out using the non-hashed plan but find a lot more upper rows going by than we
expected.  I have therefore left some minor inefficiencies in place, such as
initializing both subplans even though we will currently only use one.
parent 8875a16e
......@@ -8,7 +8,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/catalog/dependency.c,v 1.78 2008/08/07 01:11:46 tgl Exp $
* $PostgreSQL: pgsql/src/backend/catalog/dependency.c,v 1.79 2008/08/22 00:16:03 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -1464,7 +1464,7 @@ find_expr_references_walker(Node *node,
context->addrs);
/* fall through to examine arguments */
}
else if (is_subplan(node))
else if (IsA(node, SubPlan))
{
/* Extra work needed here if we ever need this case */
elog(ERROR, "already-planned subqueries not supported");
......
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.231 2008/05/15 00:17:39 tgl Exp $
* $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.232 2008/08/22 00:16:03 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -3957,6 +3957,19 @@ ExecInitExpr(Expr *node, PlanState *parent)
state = (ExprState *) sstate;
}
break;
case T_AlternativeSubPlan:
{
AlternativeSubPlan *asplan = (AlternativeSubPlan *) node;
AlternativeSubPlanState *asstate;
if (!parent)
elog(ERROR, "AlternativeSubPlan found with no parent plan");
asstate = ExecInitAlternativeSubPlan(asplan, parent);
state = (ExprState *) asstate;
}
break;
case T_FieldSelect:
{
FieldSelect *fselect = (FieldSelect *) node;
......
......@@ -7,7 +7,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/executor/nodeSubplan.c,v 1.93 2008/05/12 00:00:49 alvherre Exp $
* $PostgreSQL: pgsql/src/backend/executor/nodeSubplan.c,v 1.94 2008/08/22 00:16:03 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -29,6 +29,14 @@
#include "utils/memutils.h"
static Datum ExecSubPlan(SubPlanState *node,
ExprContext *econtext,
bool *isNull,
ExprDoneCond *isDone);
static Datum ExecAlternativeSubPlan(AlternativeSubPlanState *node,
ExprContext *econtext,
bool *isNull,
ExprDoneCond *isDone);
static Datum ExecHashSubPlan(SubPlanState *node,
ExprContext *econtext,
bool *isNull);
......@@ -45,7 +53,7 @@ static bool slotNoNulls(TupleTableSlot *slot);
* ExecSubPlan
* ----------------------------------------------------------------
*/
Datum
static Datum
ExecSubPlan(SubPlanState *node,
ExprContext *econtext,
bool *isNull,
......@@ -1066,3 +1074,83 @@ ExecReScanSetParamPlan(SubPlanState *node, PlanState *parent)
parent->chgParam = bms_add_member(parent->chgParam, paramid);
}
}
/*
* ExecInitAlternativeSubPlan
*
* Initialize for execution of one of a set of alternative subplans.
*/
AlternativeSubPlanState *
ExecInitAlternativeSubPlan(AlternativeSubPlan *asplan, PlanState *parent)
{
AlternativeSubPlanState *asstate = makeNode(AlternativeSubPlanState);
double num_calls;
SubPlan *subplan1;
SubPlan *subplan2;
Cost cost1;
Cost cost2;
asstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecAlternativeSubPlan;
asstate->xprstate.expr = (Expr *) asplan;
/*
* Initialize subplans. (Can we get away with only initializing the
* one we're going to use?)
*/
asstate->subplans = (List *) ExecInitExpr((Expr *) asplan->subplans,
parent);
/*
* Select the one to be used. For this, we need an estimate of the
* number of executions of the subplan. We use the number of output
* rows expected from the parent plan node. This is a good estimate
* if we are in the parent's targetlist, and an underestimate (but
* probably not by more than a factor of 2) if we are in the qual.
*/
num_calls = parent->plan->plan_rows;
/*
* The planner saved enough info so that we don't have to work very hard
* to estimate the total cost, given the number-of-calls estimate.
*/
Assert(list_length(asplan->subplans) == 2);
subplan1 = (SubPlan *) linitial(asplan->subplans);
subplan2 = (SubPlan *) lsecond(asplan->subplans);
cost1 = subplan1->startup_cost + num_calls * subplan1->per_call_cost;
cost2 = subplan2->startup_cost + num_calls * subplan2->per_call_cost;
if (cost1 < cost2)
asstate->active = 0;
else
asstate->active = 1;
return asstate;
}
/*
* ExecAlternativeSubPlan
*
* Execute one of a set of alternative subplans.
*
* Note: in future we might consider changing to different subplans on the
* fly, in case the original rowcount estimate turns out to be way off.
*/
static Datum
ExecAlternativeSubPlan(AlternativeSubPlanState *node,
ExprContext *econtext,
bool *isNull,
ExprDoneCond *isDone)
{
/* Just pass control to the active subplan */
SubPlanState *activesp = (SubPlanState *) list_nth(node->subplans,
node->active);
Assert(IsA(activesp, SubPlanState));
return ExecSubPlan(activesp,
econtext,
isNull,
isDone);
}
......@@ -15,7 +15,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.400 2008/08/14 18:47:58 tgl Exp $
* $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.401 2008/08/22 00:16:03 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -969,6 +969,21 @@ _copySubPlan(SubPlan *from)
COPY_NODE_FIELD(setParam);
COPY_NODE_FIELD(parParam);
COPY_NODE_FIELD(args);
COPY_SCALAR_FIELD(startup_cost);
COPY_SCALAR_FIELD(per_call_cost);
return newnode;
}
/*
* _copyAlternativeSubPlan
*/
static AlternativeSubPlan *
_copyAlternativeSubPlan(AlternativeSubPlan *from)
{
AlternativeSubPlan *newnode = makeNode(AlternativeSubPlan);
COPY_NODE_FIELD(subplans);
return newnode;
}
......@@ -3146,6 +3161,9 @@ copyObject(void *from)
case T_SubPlan:
retval = _copySubPlan(from);
break;
case T_AlternativeSubPlan:
retval = _copyAlternativeSubPlan(from);
break;
case T_FieldSelect:
retval = _copyFieldSelect(from);
break;
......
......@@ -18,7 +18,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.327 2008/08/14 18:47:58 tgl Exp $
* $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.328 2008/08/22 00:16:03 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -314,6 +314,16 @@ _equalSubPlan(SubPlan *a, SubPlan *b)
COMPARE_NODE_FIELD(setParam);
COMPARE_NODE_FIELD(parParam);
COMPARE_NODE_FIELD(args);
COMPARE_SCALAR_FIELD(startup_cost);
COMPARE_SCALAR_FIELD(per_call_cost);
return true;
}
static bool
_equalAlternativeSubPlan(AlternativeSubPlan *a, AlternativeSubPlan *b)
{
COMPARE_NODE_FIELD(subplans);
return true;
}
......@@ -2098,6 +2108,9 @@ equal(void *a, void *b)
case T_SubPlan:
retval = _equalSubPlan(a, b);
break;
case T_AlternativeSubPlan:
retval = _equalAlternativeSubPlan(a, b);
break;
case T_FieldSelect:
retval = _equalFieldSelect(a, b);
break;
......
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.334 2008/08/14 18:47:58 tgl Exp $
* $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.335 2008/08/22 00:16:03 tgl Exp $
*
* NOTES
* Every node type that can appear in stored rules' parsetrees *must*
......@@ -846,6 +846,16 @@ _outSubPlan(StringInfo str, SubPlan *node)
WRITE_NODE_FIELD(setParam);
WRITE_NODE_FIELD(parParam);
WRITE_NODE_FIELD(args);
WRITE_FLOAT_FIELD(startup_cost, "%.2f");
WRITE_FLOAT_FIELD(per_call_cost, "%.2f");
}
static void
_outAlternativeSubPlan(StringInfo str, AlternativeSubPlan *node)
{
WRITE_NODE_TYPE("ALTERNATIVESUBPLAN");
WRITE_NODE_FIELD(subplans);
}
static void
......@@ -2208,6 +2218,9 @@ _outNode(StringInfo str, void *obj)
case T_SubPlan:
_outSubPlan(str, obj);
break;
case T_AlternativeSubPlan:
_outAlternativeSubPlan(str, obj);
break;
case T_FieldSelect:
_outFieldSelect(str, obj);
break;
......
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/path/clausesel.c,v 1.92 2008/08/16 00:01:36 tgl Exp $
* $PostgreSQL: pgsql/src/backend/optimizer/path/clausesel.c,v 1.93 2008/08/22 00:16:03 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -680,7 +680,8 @@ clause_selectivity(PlannerInfo *root,
s1 = (Selectivity) 0.3333333;
}
#ifdef NOT_USED
else if (is_subplan(clause))
else if (IsA(clause, SubPlan) ||
IsA(clause, AlternativeSubPlan))
{
/*
* Just for the moment! FIX ME! - vadim 02/04/98
......
......@@ -54,7 +54,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/path/costsize.c,v 1.194 2008/08/16 00:01:36 tgl Exp $
* $PostgreSQL: pgsql/src/backend/optimizer/path/costsize.c,v 1.195 2008/08/22 00:16:03 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -1868,6 +1868,93 @@ cost_hashjoin(HashPath *path, PlannerInfo *root, SpecialJoinInfo *sjinfo)
}
/*
* cost_subplan
* Figure the costs for a SubPlan (or initplan).
*
* Note: we could dig the subplan's Plan out of the root list, but in practice
* all callers have it handy already, so we make them pass it.
*/
void
cost_subplan(PlannerInfo *root, SubPlan *subplan, Plan *plan)
{
QualCost sp_cost;
/* Figure any cost for evaluating the testexpr */
cost_qual_eval(&sp_cost,
make_ands_implicit((Expr *) subplan->testexpr),
root);
if (subplan->useHashTable)
{
/*
* If we are using a hash table for the subquery outputs, then the
* cost of evaluating the query is a one-time cost. We charge one
* cpu_operator_cost per tuple for the work of loading the hashtable,
* too.
*/
sp_cost.startup += plan->total_cost +
cpu_operator_cost * plan->plan_rows;
/*
* The per-tuple costs include the cost of evaluating the lefthand
* expressions, plus the cost of probing the hashtable. We already
* accounted for the lefthand expressions as part of the testexpr,
* and will also have counted one cpu_operator_cost for each
* comparison operator. That is probably too low for the probing
* cost, but it's hard to make a better estimate, so live with it for
* now.
*/
}
else
{
/*
* Otherwise we will be rescanning the subplan output on each
* evaluation. We need to estimate how much of the output we will
* actually need to scan. NOTE: this logic should agree with the
* tuple_fraction estimates used by make_subplan() in
* plan/subselect.c.
*/
Cost plan_run_cost = plan->total_cost - plan->startup_cost;
if (subplan->subLinkType == EXISTS_SUBLINK)
{
/* we only need to fetch 1 tuple */
sp_cost.per_tuple += plan_run_cost / plan->plan_rows;
}
else if (subplan->subLinkType == ALL_SUBLINK ||
subplan->subLinkType == ANY_SUBLINK)
{
/* assume we need 50% of the tuples */
sp_cost.per_tuple += 0.50 * plan_run_cost;
/* also charge a cpu_operator_cost per row examined */
sp_cost.per_tuple += 0.50 * plan->plan_rows * cpu_operator_cost;
}
else
{
/* assume we need all tuples */
sp_cost.per_tuple += plan_run_cost;
}
/*
* Also account for subplan's startup cost. If the subplan is
* uncorrelated or undirect correlated, AND its topmost node is a Sort
* or Material node, assume that we'll only need to pay its startup
* cost once; otherwise assume we pay the startup cost every time.
*/
if (subplan->parParam == NIL &&
(IsA(plan, Sort) ||
IsA(plan, Material)))
sp_cost.startup += plan->startup_cost;
else
sp_cost.per_tuple += plan->startup_cost;
}
subplan->startup_cost = sp_cost.startup;
subplan->per_call_cost = sp_cost.per_tuple;
}
/*
* cost_qual_eval
* Estimate the CPU costs of evaluating a WHERE clause.
......@@ -2066,79 +2153,30 @@ cost_qual_eval_walker(Node *node, cost_qual_eval_context *context)
* subplan will be executed on each evaluation, so charge accordingly.
* (Sub-selects that can be executed as InitPlans have already been
* removed from the expression.)
*
* An exception occurs when we have decided we can implement the
* subplan by hashing.
*/
SubPlan *subplan = (SubPlan *) node;
Plan *plan = planner_subplan_get_plan(context->root, subplan);
if (subplan->useHashTable)
{
/*
* If we are using a hash table for the subquery outputs, then the
* cost of evaluating the query is a one-time cost. We charge one
* cpu_operator_cost per tuple for the work of loading the
* hashtable, too.
*/
context->total.startup += plan->total_cost +
cpu_operator_cost * plan->plan_rows;
/*
* The per-tuple costs include the cost of evaluating the lefthand
* expressions, plus the cost of probing the hashtable. Recursion
* into the testexpr will handle the lefthand expressions
* properly, and will count one cpu_operator_cost for each
* comparison operator. That is probably too low for the probing
* cost, but it's hard to make a better estimate, so live with it
* for now.
*/
}
else
{
/*
* Otherwise we will be rescanning the subplan output on each
* evaluation. We need to estimate how much of the output we will
* actually need to scan. NOTE: this logic should agree with
* get_initplan_cost, below, and with the estimates used by
* make_subplan() in plan/subselect.c.
*/
Cost plan_run_cost = plan->total_cost - plan->startup_cost;
context->total.startup += subplan->startup_cost;
context->total.per_tuple += subplan->per_call_cost;
if (subplan->subLinkType == EXISTS_SUBLINK)
{
/* we only need to fetch 1 tuple */
context->total.per_tuple += plan_run_cost / plan->plan_rows;
}
else if (subplan->subLinkType == ALL_SUBLINK ||
subplan->subLinkType == ANY_SUBLINK)
{
/* assume we need 50% of the tuples */
context->total.per_tuple += 0.50 * plan_run_cost;
/* also charge a cpu_operator_cost per row examined */
context->total.per_tuple +=
0.50 * plan->plan_rows * cpu_operator_cost;
}
else
{
/* assume we need all tuples */
context->total.per_tuple += plan_run_cost;
}
/*
* We don't want to recurse into the testexpr, because it was already
* counted in the SubPlan node's costs. So we're done.
*/
return false;
}
else if (IsA(node, AlternativeSubPlan))
{
/*
* Arbitrarily use the first alternative plan for costing. (We should
* certainly only include one alternative, and we don't yet have
* enough information to know which one the executor is most likely
* to use.)
*/
AlternativeSubPlan *asplan = (AlternativeSubPlan *) node;
/*
* Also account for subplan's startup cost. If the subplan is
* uncorrelated or undirect correlated, AND its topmost node is a
* Sort or Material node, assume that we'll only need to pay its
* startup cost once; otherwise assume we pay the startup cost
* every time.
*/
if (subplan->parParam == NIL &&
(IsA(plan, Sort) ||
IsA(plan, Material)))
context->total.startup += plan->startup_cost;
else
context->total.per_tuple += plan->startup_cost;
}
return cost_qual_eval_walker((Node *) linitial(asplan->subplans),
context);
}
/* recurse into children */
......@@ -2147,43 +2185,6 @@ cost_qual_eval_walker(Node *node, cost_qual_eval_context *context)
}
/*
* get_initplan_cost
* Get the expected cost of evaluating an initPlan.
*
* Keep this in sync with cost_qual_eval_walker's handling of subplans, above,
* and with the estimates used by make_subplan() in plan/subselect.c.
*/
Cost
get_initplan_cost(PlannerInfo *root, SubPlan *subplan)
{
Cost result;
Plan *plan = planner_subplan_get_plan(root, subplan);
/* initPlans never use hashtables */
Assert(!subplan->useHashTable);
/* they are never ALL or ANY, either */
Assert(!(subplan->subLinkType == ALL_SUBLINK ||
subplan->subLinkType == ANY_SUBLINK));
if (subplan->subLinkType == EXISTS_SUBLINK)
{
/* we only need to fetch 1 tuple */
Cost plan_run_cost = plan->total_cost - plan->startup_cost;
result = plan->startup_cost;
result += plan_run_cost / plan->plan_rows;
}
else
{
/* assume we need all tuples */
result = plan->total_cost;
}
return result;
}
/*
* approx_tuple_count
* Quick-and-dirty estimation of the number of join rows passing
......
This diff is collapsed.
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.262 2008/08/14 18:47:59 tgl Exp $
* $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.263 2008/08/22 00:16:04 tgl Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
......@@ -360,7 +360,7 @@ make_ands_implicit(Expr *clause)
* are no subqueries. There mustn't be outer-aggregate references either.
*
* (If you want something like this but able to deal with subqueries,
* see rewriteManip.c's checkExprHasAggs().)
* see rewriteManip.c's contain_aggs_of_level().)
*/
bool
contain_agg_clause(Node *clause)
......@@ -566,6 +566,8 @@ expression_returns_set_walker(Node *node, void *context)
return false;
if (IsA(node, SubPlan))
return false;
if (IsA(node, AlternativeSubPlan))
return false;
if (IsA(node, ArrayExpr))
return false;
if (IsA(node, RowExpr))
......@@ -637,6 +639,8 @@ expression_returns_set_rows_walker(Node *node, double *count)
return false;
if (IsA(node, SubPlan))
return false;
if (IsA(node, AlternativeSubPlan))
return false;
if (IsA(node, ArrayExpr))
return false;
if (IsA(node, RowExpr))
......@@ -684,6 +688,7 @@ contain_subplans_walker(Node *node, void *context)
if (node == NULL)
return false;
if (IsA(node, SubPlan) ||
IsA(node, AlternativeSubPlan) ||
IsA(node, SubLink))
return true; /* abort the tree traversal and return true */
return expression_tree_walker(node, contain_subplans_walker, context);
......@@ -1012,6 +1017,8 @@ contain_nonstrict_functions_walker(Node *node, void *context)
}
if (IsA(node, SubPlan))
return true;
if (IsA(node, AlternativeSubPlan))
return true;
/* ArrayCoerceExpr is strict at the array level, regardless of elemfunc */
if (IsA(node, FieldStore))
return true;
......@@ -2308,7 +2315,8 @@ eval_const_expressions_mutator(Node *node,
break;
}
}
if (IsA(node, SubPlan))
if (IsA(node, SubPlan) ||
IsA(node, AlternativeSubPlan))
{
/*
* Return a SubPlan unchanged --- too late to do anything with it.
......@@ -4156,6 +4164,8 @@ expression_tree_walker(Node *node,
return true;
}
break;
case T_AlternativeSubPlan:
return walker(((AlternativeSubPlan *) node)->subplans, context);
case T_FieldSelect:
return walker(((FieldSelect *) node)->arg, context);
case T_FieldStore:
......@@ -4630,6 +4640,16 @@ expression_tree_mutator(Node *node,
return (Node *) newnode;
}
break;
case T_AlternativeSubPlan:
{
AlternativeSubPlan *asplan = (AlternativeSubPlan *) node;
AlternativeSubPlan *newnode;
FLATCOPY(newnode, asplan, AlternativeSubPlan);
MUTATE(newnode->subplans, asplan->subplans, List *);
return (Node *) newnode;
}
break;
case T_FieldSelect:
{
FieldSelect *fselect = (FieldSelect *) node;
......
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/util/var.c,v 1.75 2008/08/14 18:47:59 tgl Exp $
* $PostgreSQL: pgsql/src/backend/optimizer/util/var.c,v 1.76 2008/08/22 00:16:04 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -171,7 +171,7 @@ pull_varattnos_walker(Node *node, Bitmapset **varattnos)
}
/* Should not find a subquery or subplan */
Assert(!IsA(node, Query));
Assert(!is_subplan(node));
Assert(!IsA(node, SubPlan));
return expression_tree_walker(node, pull_varattnos_walker,
(void *) varattnos);
......@@ -677,7 +677,7 @@ flatten_join_alias_vars_mutator(Node *node,
return (Node *) newnode;
}
/* Already-planned tree not supported */
Assert(!is_subplan(node));
Assert(!IsA(node, SubPlan));
return expression_tree_mutator(node, flatten_join_alias_vars_mutator,
(void *) context);
......
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.229 2008/07/16 01:30:22 tgl Exp $
* $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.230 2008/08/22 00:16:04 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -1980,6 +1980,15 @@ exprType(Node *expr)
}
}
break;
case T_AlternativeSubPlan:
{
/* As above, supported for the convenience of ruleutils.c */
AlternativeSubPlan *asplan = (AlternativeSubPlan *) expr;
/* subplans should all return the same thing */
type = exprType((Node *) linitial(asplan->subplans));
}
break;
case T_FieldSelect:
type = ((FieldSelect *) expr)->resulttype;
break;
......
......@@ -7,7 +7,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/rewrite/rewriteManip.c,v 1.109 2008/08/14 20:31:29 heikki Exp $
* $PostgreSQL: pgsql/src/backend/rewrite/rewriteManip.c,v 1.110 2008/08/22 00:16:04 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -25,10 +25,10 @@
typedef struct
{
int sublevels_up;
} checkExprHasAggs_context;
} contain_aggs_of_level_context;
static bool checkExprHasAggs_walker(Node *node,
checkExprHasAggs_context *context);
static bool contain_aggs_of_level_walker(Node *node,
contain_aggs_of_level_context *context);
static bool checkExprHasSubLink_walker(Node *node, void *context);
static Relids offset_relid_set(Relids relids, int offset);
static Relids adjust_relid_set(Relids relids, int oldrelid, int newrelid);
......@@ -37,32 +37,44 @@ static Relids adjust_relid_set(Relids relids, int oldrelid, int newrelid);
/*
* checkExprHasAggs -
* Check if an expression contains an aggregate function call.
*/
bool
checkExprHasAggs(Node *node)
{
return contain_aggs_of_level(node, 0);
}
/*
* contain_aggs_of_level -
* Check if an expression contains an aggregate function call of a
* specified query level.
*
* The objective of this routine is to detect whether there are aggregates
* belonging to the initial query level. Aggregates belonging to subqueries
* belonging to the given query level. Aggregates belonging to subqueries
* or outer queries do NOT cause a true result. We must recurse into
* subqueries to detect outer-reference aggregates that logically belong to
* the initial query level.
* the specified query level.
*/
bool
checkExprHasAggs(Node *node)
contain_aggs_of_level(Node *node, int levelsup)
{
checkExprHasAggs_context context;
contain_aggs_of_level_context context;
context.sublevels_up = 0;
context.sublevels_up = levelsup;
/*
* Must be prepared to start with a Query or a bare expression tree; if
* it's a Query, we don't want to increment sublevels_up.
*/
return query_or_expression_tree_walker(node,
checkExprHasAggs_walker,
contain_aggs_of_level_walker,
(void *) &context,
0);
}
static bool
checkExprHasAggs_walker(Node *node, checkExprHasAggs_context *context)
contain_aggs_of_level_walker(Node *node,
contain_aggs_of_level_context *context)
{
if (node == NULL)
return false;
......@@ -79,12 +91,12 @@ checkExprHasAggs_walker(Node *node, checkExprHasAggs_context *context)
context->sublevels_up++;
result = query_tree_walker((Query *) node,
checkExprHasAggs_walker,
contain_aggs_of_level_walker,
(void *) context, 0);
context->sublevels_up--;
return result;
}
return expression_tree_walker(node, checkExprHasAggs_walker,
return expression_tree_walker(node, contain_aggs_of_level_walker,
(void *) context);
}
......
......@@ -9,7 +9,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.279 2008/08/02 21:32:00 tgl Exp $
* $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.280 2008/08/22 00:16:04 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -3732,6 +3732,11 @@ get_rule_expr(Node *node, deparse_context *context,
}
break;
case T_AlternativeSubPlan:
/* As above, just punt */
appendStringInfo(buf, "(alternative subplans)");
break;
case T_FieldSelect:
{
FieldSelect *fselect = (FieldSelect *) node;
......
......@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/executor/nodeSubplan.h,v 1.27 2008/01/01 19:45:57 momjian Exp $
* $PostgreSQL: pgsql/src/include/executor/nodeSubplan.h,v 1.28 2008/08/22 00:16:04 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -17,10 +17,8 @@
#include "nodes/execnodes.h"
extern SubPlanState *ExecInitSubPlan(SubPlan *subplan, PlanState *parent);
extern Datum ExecSubPlan(SubPlanState *node,
ExprContext *econtext,
bool *isNull,
ExprDoneCond *isDone);
extern AlternativeSubPlanState *ExecInitAlternativeSubPlan(AlternativeSubPlan *asplan, PlanState *parent);
extern void ExecReScanSetParamPlan(SubPlanState *node, PlanState *parent);
......
......@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/nodes/execnodes.h,v 1.186 2008/08/07 03:04:03 tgl Exp $
* $PostgreSQL: pgsql/src/include/nodes/execnodes.h,v 1.187 2008/08/22 00:16:04 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -627,6 +627,17 @@ typedef struct SubPlanState
FmgrInfo *cur_eq_funcs; /* equality functions for LHS vs. table */
} SubPlanState;
/* ----------------
* AlternativeSubPlanState node
* ----------------
*/
typedef struct AlternativeSubPlanState
{
ExprState xprstate;
List *subplans; /* states of alternative subplans */
int active; /* list index of the one we're using */
} AlternativeSubPlanState;
/* ----------------
* FieldSelectState node
* ----------------
......
......@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/nodes/nodes.h,v 1.208 2008/08/14 18:48:00 tgl Exp $
* $PostgreSQL: pgsql/src/include/nodes/nodes.h,v 1.209 2008/08/22 00:16:04 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -118,6 +118,7 @@ typedef enum NodeTag
T_BoolExpr,
T_SubLink,
T_SubPlan,
T_AlternativeSubPlan,
T_FieldSelect,
T_FieldStore,
T_RelabelType,
......@@ -160,6 +161,7 @@ typedef enum NodeTag
T_ScalarArrayOpExprState,
T_BoolExprState,
T_SubPlanState,
T_AlternativeSubPlanState,
T_FieldSelectState,
T_FieldStoreState,
T_CoerceViaIOState,
......
......@@ -10,7 +10,7 @@
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/nodes/primnodes.h,v 1.138 2008/08/02 21:32:00 tgl Exp $
* $PostgreSQL: pgsql/src/include/nodes/primnodes.h,v 1.139 2008/08/22 00:16:04 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -445,7 +445,8 @@ typedef struct SubLink
*
* If the sub-select becomes an initplan rather than a subplan, the executable
* expression is part of the outer plan's expression tree (and the SubPlan
* node itself is not). In this case testexpr is NULL to avoid duplication.
* node itself is not, but rather is found in the outer plan's initPlan
* list). In this case testexpr is NULL to avoid duplication.
*
* The planner also derives lists of the values that need to be passed into
* and out of the subplan. Input values are represented as a list "args" of
......@@ -457,6 +458,10 @@ typedef struct SubLink
* is an initplan; they are listed in order by sub-select output column
* position. (parParam and setParam are integer Lists, not Bitmapsets,
* because their ordering is significant.)
*
* Also, the planner computes startup and per-call costs for use of the
* SubPlan. Note that these include the cost of the subquery proper,
* evaluation of the testexpr if any, and any hashtable management overhead.
*/
typedef struct SubPlan
{
......@@ -482,8 +487,25 @@ typedef struct SubPlan
* Params for parent plan */
List *parParam; /* indices of input Params from parent plan */
List *args; /* exprs to pass as parParam values */
/* Estimated execution costs: */
Cost startup_cost; /* one-time setup cost */
Cost per_call_cost; /* cost for each subplan evaluation */
} SubPlan;
/*
* AlternativeSubPlan - expression node for a choice among SubPlans
*
* The subplans are given as a List so that the node definition need not
* change if there's ever more than two alternatives. For the moment,
* though, there are always exactly two; and the first one is the fast-start
* plan.
*/
typedef struct AlternativeSubPlan
{
Expr xpr;
List *subplans; /* SubPlan(s) with equivalent results */
} AlternativeSubPlan;
/* ----------------
* FieldSelect
*
......
......@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/optimizer/clauses.h,v 1.92 2008/08/14 18:48:00 tgl Exp $
* $PostgreSQL: pgsql/src/include/optimizer/clauses.h,v 1.93 2008/08/22 00:16:04 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -19,7 +19,6 @@
#define is_opclause(clause) ((clause) != NULL && IsA(clause, OpExpr))
#define is_funcclause(clause) ((clause) != NULL && IsA(clause, FuncExpr))
#define is_subplan(clause) ((clause) != NULL && IsA(clause, SubPlan))
typedef struct
{
......
......@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/optimizer/cost.h,v 1.91 2008/08/14 18:48:00 tgl Exp $
* $PostgreSQL: pgsql/src/include/optimizer/cost.h,v 1.92 2008/08/22 00:16:04 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -93,9 +93,9 @@ extern void cost_mergejoin(MergePath *path, PlannerInfo *root,
SpecialJoinInfo *sjinfo);
extern void cost_hashjoin(HashPath *path, PlannerInfo *root,
SpecialJoinInfo *sjinfo);
extern void cost_subplan(PlannerInfo *root, SubPlan *subplan, Plan *plan);
extern void cost_qual_eval(QualCost *cost, List *quals, PlannerInfo *root);
extern void cost_qual_eval_node(QualCost *cost, Node *qual, PlannerInfo *root);
extern Cost get_initplan_cost(PlannerInfo *root, SubPlan *subplan);
extern void set_baserel_size_estimates(PlannerInfo *root, RelOptInfo *rel);
extern void set_joinrel_size_estimates(PlannerInfo *root, RelOptInfo *rel,
RelOptInfo *outer_rel,
......
......@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/rewrite/rewriteManip.h,v 1.45 2008/08/14 20:31:29 heikki Exp $
* $PostgreSQL: pgsql/src/include/rewrite/rewriteManip.h,v 1.46 2008/08/22 00:16:04 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -35,6 +35,7 @@ extern Query *getInsertSelectQuery(Query *parsetree, Query ***subquery_ptr);
extern void AddQual(Query *parsetree, Node *qual);
extern void AddInvertedQual(Query *parsetree, Node *qual);
extern bool contain_aggs_of_level(Node *node, int levelsup);
extern bool checkExprHasAggs(Node *node);
extern bool checkExprHasSubLink(Node *node);
......
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