Commit 41efb834 authored by Tom Lane's avatar Tom Lane

Move resolution of AlternativeSubPlan choices to the planner.

When commit bd3dadda introduced AlternativeSubPlans, I had some
ambitions towards allowing the choice of subplan to change during
execution.  That has not happened, or even been thought about, in the
ensuing twelve years; so it seems like a failed experiment.  So let's
rip that out and resolve the choice of subplan at the end of planning
(in setrefs.c) rather than during executor startup.  This has a number
of positive benefits:

* Removal of a few hundred lines of executor code, since
AlternativeSubPlans need no longer be supported there.

* Removal of executor-startup overhead (particularly, initialization
of subplans that won't be used).

* Removal of incidental costs of having a larger plan tree, such as
tree-scanning and copying costs in the plancache; not to mention
setrefs.c's own costs of processing the discarded subplans.

* EXPLAIN no longer has to print a weird (and undocumented)
representation of an AlternativeSubPlan choice; it sees only the
subplan actually used.  This should mean less confusion for users.

* Since setrefs.c knows which subexpression of a plan node it's
working on at any instant, it's possible to adjust the estimated
number of executions of the subplan based on that.  For example,
we should usually estimate more executions of a qual expression
than a targetlist expression.  The implementation used here is
pretty simplistic, because we don't want to expend a lot of cycles
on the issue; but it's better than ignoring the point entirely,
as the executor had to.

That last point might possibly result in shifting the choice
between hashed and non-hashed EXISTS subplans in a few cases,
but in general this patch isn't meant to change planner choices.
Since we're doing the resolution so late, it's really impossible
to change any plan choices outside the AlternativeSubPlan itself.

Patch by me; thanks to David Rowley for review.

Discussion: https://postgr.es/m/1992952.1592785225@sss.pgh.pa.us
parent 3c881995
......@@ -1104,23 +1104,6 @@ ExecInitExprRec(Expr *node, ExprState *state,
break;
}
case T_AlternativeSubPlan:
{
AlternativeSubPlan *asplan = (AlternativeSubPlan *) node;
AlternativeSubPlanState *asstate;
if (!state->parent)
elog(ERROR, "AlternativeSubPlan found with no parent plan");
asstate = ExecInitAlternativeSubPlan(asplan, state->parent);
scratch.opcode = EEOP_ALTERNATIVE_SUBPLAN;
scratch.d.alternative_subplan.asstate = asstate;
ExprEvalPushStep(state, &scratch);
break;
}
case T_FieldSelect:
{
FieldSelect *fselect = (FieldSelect *) node;
......
......@@ -431,7 +431,6 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
&&CASE_EEOP_GROUPING_FUNC,
&&CASE_EEOP_WINDOW_FUNC,
&&CASE_EEOP_SUBPLAN,
&&CASE_EEOP_ALTERNATIVE_SUBPLAN,
&&CASE_EEOP_AGG_STRICT_DESERIALIZE,
&&CASE_EEOP_AGG_DESERIALIZE,
&&CASE_EEOP_AGG_STRICT_INPUT_CHECK_ARGS,
......@@ -1536,14 +1535,6 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
EEO_NEXT();
}
EEO_CASE(EEOP_ALTERNATIVE_SUBPLAN)
{
/* too complex for an inline implementation */
ExecEvalAlternativeSubPlan(state, op, econtext);
EEO_NEXT();
}
/* evaluate a strict aggregate deserialization function */
EEO_CASE(EEOP_AGG_STRICT_DESERIALIZE)
{
......@@ -3868,20 +3859,6 @@ ExecEvalSubPlan(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
*op->resvalue = ExecSubPlan(sstate, econtext, op->resnull);
}
/*
* Hand off evaluation of an alternative subplan to nodeSubplan.c
*/
void
ExecEvalAlternativeSubPlan(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
{
AlternativeSubPlanState *asstate = op->d.alternative_subplan.asstate;
/* could potentially be nested, so make sure there's enough stack */
check_stack_depth();
*op->resvalue = ExecAlternativeSubPlan(asstate, econtext, op->resnull);
}
/*
* Evaluate a wholerow Var expression.
*
......
......@@ -1303,83 +1303,3 @@ 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;
ListCell *lc;
asstate->subplan = asplan;
/*
* Initialize subplans. (Can we get away with only initializing the one
* we're going to use?)
*/
foreach(lc, asplan->subplans)
{
SubPlan *sp = lfirst_node(SubPlan, lc);
SubPlanState *sps = ExecInitSubPlan(sp, parent);
asstate->subplans = lappend(asstate->subplans, sps);
parent->subPlan = lappend(parent->subPlan, sps);
}
/*
* 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.
*/
Datum
ExecAlternativeSubPlan(AlternativeSubPlanState *node,
ExprContext *econtext,
bool *isNull)
{
/* Just pass control to the active subplan */
SubPlanState *activesp = list_nth_node(SubPlanState,
node->subplans, node->active);
return ExecSubPlan(activesp, econtext, isNull);
}
......@@ -1918,12 +1918,6 @@ llvm_compile_expr(ExprState *state)
LLVMBuildBr(b, opblocks[opno + 1]);
break;
case EEOP_ALTERNATIVE_SUBPLAN:
build_EvalXFunc(b, mod, "ExecEvalAlternativeSubPlan",
v_state, op, v_econtext);
LLVMBuildBr(b, opblocks[opno + 1]);
break;
case EEOP_AGG_STRICT_DESERIALIZE:
case EEOP_AGG_DESERIALIZE:
{
......
......@@ -102,7 +102,6 @@ void *referenced_functions[] =
ExecAggTransReparent,
ExecEvalAggOrderedTransDatum,
ExecEvalAggOrderedTransTuple,
ExecEvalAlternativeSubPlan,
ExecEvalArrayCoerce,
ExecEvalArrayExpr,
ExecEvalConstraintCheck,
......
......@@ -2254,6 +2254,7 @@ _outPlannerInfo(StringInfo str, const PlannerInfo *node)
WRITE_BOOL_FIELD(hasLateralRTEs);
WRITE_BOOL_FIELD(hasHavingQual);
WRITE_BOOL_FIELD(hasPseudoConstantQuals);
WRITE_BOOL_FIELD(hasAlternativeSubPlans);
WRITE_BOOL_FIELD(hasRecursion);
WRITE_INT_FIELD(wt_param_id);
WRITE_BITMAPSET_FIELD(curOuterRels);
......
......@@ -629,6 +629,8 @@ subquery_planner(PlannerGlobal *glob, Query *parse,
root->minmax_aggs = NIL;
root->qual_security_level = 0;
root->inhTargetKind = INHKIND_NONE;
root->hasPseudoConstantQuals = false;
root->hasAlternativeSubPlans = false;
root->hasRecursion = hasRecursion;
if (hasRecursion)
root->wt_param_id = assign_special_exec_param(root);
......@@ -759,9 +761,6 @@ subquery_planner(PlannerGlobal *glob, Query *parse,
*/
root->hasHavingQual = (parse->havingQual != NULL);
/* Clear this flag; might get set in distribute_qual_to_rels */
root->hasPseudoConstantQuals = false;
/*
* Do expression preprocessing on targetlist and quals, as well as other
* random expressions in the querytree. Note that we do not need to
......
This diff is collapsed.
......@@ -81,6 +81,7 @@ static Node *convert_testexpr(PlannerInfo *root,
static Node *convert_testexpr_mutator(Node *node,
convert_testexpr_context *context);
static bool subplan_is_hashable(Plan *plan);
static bool subpath_is_hashable(Path *path);
static bool testexpr_is_hashable(Node *testexpr, List *param_ids);
static bool test_opexpr_is_hashable(OpExpr *testexpr, List *param_ids);
static bool hash_ok_operator(OpExpr *expr);
......@@ -247,7 +248,7 @@ make_subplan(PlannerInfo *root, Query *orig_subquery,
* likely to be better (it depends on the expected number of executions of
* the EXISTS qual, and we are much too early in planning the outer query
* to be able to guess that). So we generate both plans, if possible, and
* leave it to the executor to decide which to use.
* leave it to setrefs.c to decide which to use.
*/
if (simple_exists && IsA(result, SubPlan))
{
......@@ -273,20 +274,20 @@ make_subplan(PlannerInfo *root, Query *orig_subquery,
plan_params = root->plan_params;
root->plan_params = NIL;
/* Select best Path and turn it into a Plan */
/* Select best Path */
final_rel = fetch_upper_rel(subroot, UPPERREL_FINAL, NULL);
best_path = final_rel->cheapest_total_path;
plan = create_plan(subroot, best_path);
/* Now we can check if it'll fit in hash_mem */
/* XXX can we check this at the Path stage? */
if (subplan_is_hashable(plan))
if (subpath_is_hashable(best_path))
{
SubPlan *hashplan;
AlternativeSubPlan *asplan;
/* OK, convert to SubPlan format. */
/* OK, finish planning the ANY subquery */
plan = create_plan(subroot, best_path);
/* ... and convert to SubPlan format */
hashplan = castNode(SubPlan,
build_subplan(root, plan, subroot,
plan_params,
......@@ -298,10 +299,11 @@ make_subplan(PlannerInfo *root, Query *orig_subquery,
Assert(hashplan->parParam == NIL);
Assert(hashplan->useHashTable);
/* Leave it to the executor to decide which plan to use */
/* Leave it to setrefs.c to decide which plan to use */
asplan = makeNode(AlternativeSubPlan);
asplan->subplans = list_make2(result, hashplan);
result = (Node *) asplan;
root->hasAlternativeSubPlans = true;
}
}
}
......@@ -714,6 +716,9 @@ convert_testexpr_mutator(Node *node,
/*
* subplan_is_hashable: can we implement an ANY subplan by hashing?
*
* This is not responsible for checking whether the combining testexpr
* is suitable for hashing. We only look at the subquery itself.
*/
static bool
subplan_is_hashable(Plan *plan)
......@@ -735,6 +740,31 @@ subplan_is_hashable(Plan *plan)
return true;
}
/*
* subpath_is_hashable: can we implement an ANY subplan by hashing?
*
* Identical to subplan_is_hashable, but work from a Path for the subplan.
*/
static bool
subpath_is_hashable(Path *path)
{
double subquery_size;
int hash_mem = get_hash_mem();
/*
* The estimated size of the subquery result must fit in hash_mem. (Note:
* we use heap tuple overhead here even though the tuples will actually be
* stored as MinimalTuples; this provides some fudge factor for hashtable
* overhead.)
*/
subquery_size = path->rows *
(MAXALIGN(path->pathtarget->width) + MAXALIGN(SizeofHeapTupleHeader));
if (subquery_size > hash_mem * 1024L)
return false;
return true;
}
/*
* testexpr_is_hashable: is an ANY SubLink's test expression hashable?
*
......
......@@ -8192,7 +8192,12 @@ get_rule_expr(Node *node, deparse_context *context,
AlternativeSubPlan *asplan = (AlternativeSubPlan *) node;
ListCell *lc;
/* As above, this can only happen during EXPLAIN */
/*
* This case cannot be reached in normal usage, since no
* AlternativeSubPlan can appear either in parsetrees or
* finished plan trees. We keep it just in case somebody
* wants to use this code to print planner data structures.
*/
appendStringInfoString(buf, "(alternatives: ");
foreach(lc, asplan->subplans)
{
......
......@@ -218,7 +218,6 @@ typedef enum ExprEvalOp
EEOP_GROUPING_FUNC,
EEOP_WINDOW_FUNC,
EEOP_SUBPLAN,
EEOP_ALTERNATIVE_SUBPLAN,
/* aggregation related nodes */
EEOP_AGG_STRICT_DESERIALIZE,
......@@ -589,13 +588,6 @@ typedef struct ExprEvalStep
SubPlanState *sstate;
} subplan;
/* for EEOP_ALTERNATIVE_SUBPLAN */
struct
{
/* out-of-line state, created by nodeSubplan.c */
AlternativeSubPlanState *asstate;
} alternative_subplan;
/* for EEOP_AGG_*DESERIALIZE */
struct
{
......@@ -734,8 +726,6 @@ extern void ExecEvalXmlExpr(ExprState *state, ExprEvalStep *op);
extern void ExecEvalGroupingFunc(ExprState *state, ExprEvalStep *op);
extern void ExecEvalSubPlan(ExprState *state, ExprEvalStep *op,
ExprContext *econtext);
extern void ExecEvalAlternativeSubPlan(ExprState *state, ExprEvalStep *op,
ExprContext *econtext);
extern void ExecEvalWholeRowVar(ExprState *state, ExprEvalStep *op,
ExprContext *econtext);
extern void ExecEvalSysVar(ExprState *state, ExprEvalStep *op,
......
......@@ -18,12 +18,8 @@
extern SubPlanState *ExecInitSubPlan(SubPlan *subplan, PlanState *parent);
extern AlternativeSubPlanState *ExecInitAlternativeSubPlan(AlternativeSubPlan *asplan, PlanState *parent);
extern Datum ExecSubPlan(SubPlanState *node, ExprContext *econtext, bool *isNull);
extern Datum ExecAlternativeSubPlan(AlternativeSubPlanState *node, ExprContext *econtext, bool *isNull);
extern void ExecReScanSetParamPlan(SubPlanState *node, PlanState *parent);
extern void ExecSetParamPlan(SubPlanState *node, ExprContext *econtext);
......
......@@ -877,18 +877,6 @@ typedef struct SubPlanState
ExprState *cur_eq_comp; /* equality comparator for LHS vs. table */
} SubPlanState;
/* ----------------
* AlternativeSubPlanState node
* ----------------
*/
typedef struct AlternativeSubPlanState
{
NodeTag type;
AlternativeSubPlan *subplan; /* expression plan node */
List *subplans; /* SubPlanStates of alternative subplans */
int active; /* list index of the one we're using */
} AlternativeSubPlanState;
/*
* DomainConstraintState - one item to check during CoerceToDomain
*
......
......@@ -213,7 +213,6 @@ typedef enum NodeTag
T_WindowFuncExprState,
T_SetExprState,
T_SubPlanState,
T_AlternativeSubPlanState,
T_DomainConstraintState,
/*
......
......@@ -347,6 +347,7 @@ struct PlannerInfo
bool hasHavingQual; /* true if havingQual was non-null */
bool hasPseudoConstantQuals; /* true if any RestrictInfo has
* pseudoconstant = true */
bool hasAlternativeSubPlans; /* true if we've made any of those */
bool hasRecursion; /* true if planning a recursive WITH item */
/* These fields are used only when hasRecursion is true: */
......
......@@ -736,6 +736,9 @@ typedef struct SubPlan
/*
* AlternativeSubPlan - expression node for a choice among SubPlans
*
* This is used only transiently during planning: by the time the plan
* reaches the executor, all AlternativeSubPlan nodes have been removed.
*
* 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
......
......@@ -50,14 +50,12 @@ explain (costs off) insert into insertconflicttest values(0, 'Crowberry') on con
Insert on insertconflicttest
Conflict Resolution: UPDATE
Conflict Arbiter Indexes: op_index_key, collation_index_key, both_index_key
Conflict Filter: (alternatives: SubPlan 1 or hashed SubPlan 2)
Conflict Filter: (SubPlan 1)
-> Result
SubPlan 1
-> Index Only Scan using both_index_expr_key on insertconflicttest ii
Index Cond: (key = excluded.key)
SubPlan 2
-> Seq Scan on insertconflicttest ii_1
(10 rows)
(8 rows)
-- Neither collation nor operator class specifications are required --
-- supplying them merely *limits* matches to indexes with matching opclasses
......
......@@ -874,6 +874,53 @@ select * from int8_tbl where q1 in (select c1 from inner_text);
(2 rows)
rollback; -- to get rid of the bogus operator
--
-- Test resolution of hashed vs non-hashed implementation of EXISTS subplan
--
explain (costs off)
select count(*) from tenk1 t
where (exists(select 1 from tenk1 k where k.unique1 = t.unique2) or ten < 0);
QUERY PLAN
--------------------------------------------------------------
Aggregate
-> Seq Scan on tenk1 t
Filter: ((hashed SubPlan 2) OR (ten < 0))
SubPlan 2
-> Index Only Scan using tenk1_unique1 on tenk1 k
(5 rows)
select count(*) from tenk1 t
where (exists(select 1 from tenk1 k where k.unique1 = t.unique2) or ten < 0);
count
-------
10000
(1 row)
explain (costs off)
select count(*) from tenk1 t
where (exists(select 1 from tenk1 k where k.unique1 = t.unique2) or ten < 0)
and thousand = 1;
QUERY PLAN
--------------------------------------------------------------
Aggregate
-> Bitmap Heap Scan on tenk1 t
Recheck Cond: (thousand = 1)
Filter: ((SubPlan 1) OR (ten < 0))
-> Bitmap Index Scan on tenk1_thous_tenthous
Index Cond: (thousand = 1)
SubPlan 1
-> Index Only Scan using tenk1_unique1 on tenk1 k
Index Cond: (unique1 = t.unique2)
(9 rows)
select count(*) from tenk1 t
where (exists(select 1 from tenk1 k where k.unique1 = t.unique2) or ten < 0)
and thousand = 1;
count
-------
10
(1 row)
--
-- Test case for planner bug with nested EXISTS handling
--
......
......@@ -1869,9 +1869,7 @@ EXPLAIN (costs off) INSERT INTO rw_view1 VALUES (5);
SubPlan 1
-> Index Only Scan using ref_tbl_pkey on ref_tbl r
Index Cond: (a = b.a)
SubPlan 2
-> Seq Scan on ref_tbl r_1
(7 rows)
(5 rows)
EXPLAIN (costs off) UPDATE rw_view1 SET a = a + 5;
QUERY PLAN
......@@ -1885,9 +1883,7 @@ EXPLAIN (costs off) UPDATE rw_view1 SET a = a + 5;
SubPlan 1
-> Index Only Scan using ref_tbl_pkey on ref_tbl r_1
Index Cond: (a = b.a)
SubPlan 2
-> Seq Scan on ref_tbl r_2
(11 rows)
(9 rows)
DROP TABLE base_tbl, ref_tbl CASCADE;
NOTICE: drop cascades to view rw_view1
......@@ -2301,8 +2297,8 @@ SELECT * FROM v1 WHERE a=8;
EXPLAIN (VERBOSE, COSTS OFF)
UPDATE v1 SET a=100 WHERE snoop(a) AND leakproof(a) AND a < 7 AND a != 6;
QUERY PLAN
---------------------------------------------------------------------------------------------------------------------------
QUERY PLAN
-----------------------------------------------------------------------------------------
Update on public.t1
Update on public.t1
Update on public.t11 t1_1
......@@ -2311,32 +2307,26 @@ UPDATE v1 SET a=100 WHERE snoop(a) AND leakproof(a) AND a < 7 AND a != 6;
-> Index Scan using t1_a_idx on public.t1
Output: 100, t1.b, t1.c, t1.ctid
Index Cond: ((t1.a > 5) AND (t1.a < 7))
Filter: ((t1.a <> 6) AND (alternatives: SubPlan 1 or hashed SubPlan 2) AND snoop(t1.a) AND leakproof(t1.a))
Filter: ((t1.a <> 6) AND (SubPlan 1) AND snoop(t1.a) AND leakproof(t1.a))
SubPlan 1
-> Append
-> Seq Scan on public.t12 t12_1
Filter: (t12_1.a = t1.a)
-> Seq Scan on public.t111 t12_2
Filter: (t12_2.a = t1.a)
SubPlan 2
-> Append
-> Seq Scan on public.t12 t12_4
Output: t12_4.a
-> Seq Scan on public.t111 t12_5
Output: t12_5.a
-> Index Scan using t11_a_idx on public.t11 t1_1
Output: 100, t1_1.b, t1_1.c, t1_1.d, t1_1.ctid
Index Cond: ((t1_1.a > 5) AND (t1_1.a < 7))
Filter: ((t1_1.a <> 6) AND (alternatives: SubPlan 1 or hashed SubPlan 2) AND snoop(t1_1.a) AND leakproof(t1_1.a))
Filter: ((t1_1.a <> 6) AND (SubPlan 1) AND snoop(t1_1.a) AND leakproof(t1_1.a))
-> Index Scan using t12_a_idx on public.t12 t1_2
Output: 100, t1_2.b, t1_2.c, t1_2.e, t1_2.ctid
Index Cond: ((t1_2.a > 5) AND (t1_2.a < 7))
Filter: ((t1_2.a <> 6) AND (alternatives: SubPlan 1 or hashed SubPlan 2) AND snoop(t1_2.a) AND leakproof(t1_2.a))
Filter: ((t1_2.a <> 6) AND (SubPlan 1) AND snoop(t1_2.a) AND leakproof(t1_2.a))
-> Index Scan using t111_a_idx on public.t111 t1_3
Output: 100, t1_3.b, t1_3.c, t1_3.d, t1_3.e, t1_3.ctid
Index Cond: ((t1_3.a > 5) AND (t1_3.a < 7))
Filter: ((t1_3.a <> 6) AND (alternatives: SubPlan 1 or hashed SubPlan 2) AND snoop(t1_3.a) AND leakproof(t1_3.a))
(33 rows)
Filter: ((t1_3.a <> 6) AND (SubPlan 1) AND snoop(t1_3.a) AND leakproof(t1_3.a))
(27 rows)
UPDATE v1 SET a=100 WHERE snoop(a) AND leakproof(a) AND a < 7 AND a != 6;
SELECT * FROM v1 WHERE a=100; -- Nothing should have been changed to 100
......@@ -2351,8 +2341,8 @@ SELECT * FROM t1 WHERE a=100; -- Nothing should have been changed to 100
EXPLAIN (VERBOSE, COSTS OFF)
UPDATE v1 SET a=a+1 WHERE snoop(a) AND leakproof(a) AND a = 8;
QUERY PLAN
---------------------------------------------------------------------------------------------------------
QUERY PLAN
-------------------------------------------------------------------------
Update on public.t1
Update on public.t1
Update on public.t11 t1_1
......@@ -2361,32 +2351,26 @@ UPDATE v1 SET a=a+1 WHERE snoop(a) AND leakproof(a) AND a = 8;
-> Index Scan using t1_a_idx on public.t1
Output: (t1.a + 1), t1.b, t1.c, t1.ctid
Index Cond: ((t1.a > 5) AND (t1.a = 8))
Filter: ((alternatives: SubPlan 1 or hashed SubPlan 2) AND snoop(t1.a) AND leakproof(t1.a))
Filter: ((SubPlan 1) AND snoop(t1.a) AND leakproof(t1.a))
SubPlan 1
-> Append
-> Seq Scan on public.t12 t12_1
Filter: (t12_1.a = t1.a)
-> Seq Scan on public.t111 t12_2
Filter: (t12_2.a = t1.a)
SubPlan 2
-> Append
-> Seq Scan on public.t12 t12_4
Output: t12_4.a
-> Seq Scan on public.t111 t12_5
Output: t12_5.a
-> Index Scan using t11_a_idx on public.t11 t1_1
Output: (t1_1.a + 1), t1_1.b, t1_1.c, t1_1.d, t1_1.ctid
Index Cond: ((t1_1.a > 5) AND (t1_1.a = 8))
Filter: ((alternatives: SubPlan 1 or hashed SubPlan 2) AND snoop(t1_1.a) AND leakproof(t1_1.a))
Filter: ((SubPlan 1) AND snoop(t1_1.a) AND leakproof(t1_1.a))
-> Index Scan using t12_a_idx on public.t12 t1_2
Output: (t1_2.a + 1), t1_2.b, t1_2.c, t1_2.e, t1_2.ctid
Index Cond: ((t1_2.a > 5) AND (t1_2.a = 8))
Filter: ((alternatives: SubPlan 1 or hashed SubPlan 2) AND snoop(t1_2.a) AND leakproof(t1_2.a))
Filter: ((SubPlan 1) AND snoop(t1_2.a) AND leakproof(t1_2.a))
-> Index Scan using t111_a_idx on public.t111 t1_3
Output: (t1_3.a + 1), t1_3.b, t1_3.c, t1_3.d, t1_3.e, t1_3.ctid
Index Cond: ((t1_3.a > 5) AND (t1_3.a = 8))
Filter: ((alternatives: SubPlan 1 or hashed SubPlan 2) AND snoop(t1_3.a) AND leakproof(t1_3.a))
(33 rows)
Filter: ((SubPlan 1) AND snoop(t1_3.a) AND leakproof(t1_3.a))
(27 rows)
UPDATE v1 SET a=a+1 WHERE snoop(a) AND leakproof(a) AND a = 8;
NOTICE: snooped value: 8
......
......@@ -509,6 +509,23 @@ select * from int8_tbl where q1 in (select c1 from inner_text);
rollback; -- to get rid of the bogus operator
--
-- Test resolution of hashed vs non-hashed implementation of EXISTS subplan
--
explain (costs off)
select count(*) from tenk1 t
where (exists(select 1 from tenk1 k where k.unique1 = t.unique2) or ten < 0);
select count(*) from tenk1 t
where (exists(select 1 from tenk1 k where k.unique1 = t.unique2) or ten < 0);
explain (costs off)
select count(*) from tenk1 t
where (exists(select 1 from tenk1 k where k.unique1 = t.unique2) or ten < 0)
and thousand = 1;
select count(*) from tenk1 t
where (exists(select 1 from tenk1 k where k.unique1 = t.unique2) or ten < 0)
and thousand = 1;
--
-- Test case for planner bug with nested EXISTS handling
--
......
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