aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/backend/executor/execExpr.c17
-rw-r--r--src/backend/executor/execExprInterp.c23
-rw-r--r--src/backend/executor/nodeSubplan.c80
-rw-r--r--src/backend/jit/llvm/llvmjit_expr.c6
-rw-r--r--src/backend/jit/llvm/llvmjit_types.c1
-rw-r--r--src/backend/nodes/outfuncs.c1
-rw-r--r--src/backend/optimizer/plan/planner.c5
-rw-r--r--src/backend/optimizer/plan/setrefs.c372
-rw-r--r--src/backend/optimizer/plan/subselect.c46
-rw-r--r--src/backend/utils/adt/ruleutils.c7
-rw-r--r--src/include/executor/execExpr.h10
-rw-r--r--src/include/executor/nodeSubplan.h4
-rw-r--r--src/include/nodes/execnodes.h12
-rw-r--r--src/include/nodes/nodes.h1
-rw-r--r--src/include/nodes/pathnodes.h1
-rw-r--r--src/include/nodes/primnodes.h3
-rw-r--r--src/test/regress/expected/insert_conflict.out6
-rw-r--r--src/test/regress/expected/subselect.out47
-rw-r--r--src/test/regress/expected/updatable_views.out48
-rw-r--r--src/test/regress/sql/subselect.sql17
20 files changed, 412 insertions, 295 deletions
diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c
index 236413f62aa..868f8b0858f 100644
--- a/src/backend/executor/execExpr.c
+++ b/src/backend/executor/execExpr.c
@@ -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;
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index b812bbaceef..26c2b496321 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -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)
{
@@ -3869,20 +3860,6 @@ ExecEvalSubPlan(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
}
/*
- * 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.
*
* Returns a Datum whose value is the value of a whole-row range variable
diff --git a/src/backend/executor/nodeSubplan.c b/src/backend/executor/nodeSubplan.c
index 9a7962518ee..9a706df5f06 100644
--- a/src/backend/executor/nodeSubplan.c
+++ b/src/backend/executor/nodeSubplan.c
@@ -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);
-}
diff --git a/src/backend/jit/llvm/llvmjit_expr.c b/src/backend/jit/llvm/llvmjit_expr.c
index cca5c117a0e..eb1dea658cb 100644
--- a/src/backend/jit/llvm/llvmjit_expr.c
+++ b/src/backend/jit/llvm/llvmjit_expr.c
@@ -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:
{
diff --git a/src/backend/jit/llvm/llvmjit_types.c b/src/backend/jit/llvm/llvmjit_types.c
index 0a93d5f6658..1ed3cafa2f2 100644
--- a/src/backend/jit/llvm/llvmjit_types.c
+++ b/src/backend/jit/llvm/llvmjit_types.c
@@ -102,7 +102,6 @@ void *referenced_functions[] =
ExecAggTransReparent,
ExecEvalAggOrderedTransDatum,
ExecEvalAggOrderedTransTuple,
- ExecEvalAlternativeSubPlan,
ExecEvalArrayCoerce,
ExecEvalArrayExpr,
ExecEvalConstraintCheck,
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index e2f177515da..f0386480ab8 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -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);
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 8007e205ed7..3e2b4965c4a 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -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
diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c
index baefe0e9461..dd8e2e966dd 100644
--- a/src/backend/optimizer/plan/setrefs.c
+++ b/src/backend/optimizer/plan/setrefs.c
@@ -49,6 +49,7 @@ typedef struct
{
PlannerInfo *root;
int rtoffset;
+ double num_exec;
} fix_scan_expr_context;
typedef struct
@@ -58,6 +59,7 @@ typedef struct
indexed_tlist *inner_itlist;
Index acceptable_rel;
int rtoffset;
+ double num_exec;
} fix_join_expr_context;
typedef struct
@@ -66,9 +68,29 @@ typedef struct
indexed_tlist *subplan_itlist;
Index newvarno;
int rtoffset;
+ double num_exec;
} fix_upper_expr_context;
/*
+ * Selecting the best alternative in an AlternativeSubPlan expression requires
+ * estimating how many times that expression will be evaluated. For an
+ * expression in a plan node's targetlist, the plan's estimated number of
+ * output rows is clearly what to use, but for an expression in a qual it's
+ * far less clear. Since AlternativeSubPlans aren't heavily used, we don't
+ * want to expend a lot of cycles making such estimates. What we use is twice
+ * the number of output rows. That's not entirely unfounded: we know that
+ * clause_selectivity() would fall back to a default selectivity estimate
+ * of 0.5 for any SubPlan, so if the qual containing the SubPlan is the last
+ * to be applied (which it likely would be, thanks to order_qual_clauses()),
+ * this matches what we could have estimated in a far more laborious fashion.
+ * Obviously there are many other scenarios, but it's probably not worth the
+ * trouble to try to improve on this estimate, especially not when we don't
+ * have a better estimate for the selectivity of the SubPlan qual itself.
+ */
+#define NUM_EXEC_TLIST(parentplan) ((parentplan)->plan_rows)
+#define NUM_EXEC_QUAL(parentplan) ((parentplan)->plan_rows * 2.0)
+
+/*
* Check if a Const node is a regclass value. We accept plain OID too,
* since a regclass Const will get folded to that type if it's an argument
* to oideq or similar operators. (This might result in some extraneous
@@ -79,8 +101,8 @@ typedef struct
(((con)->consttype == REGCLASSOID || (con)->consttype == OIDOID) && \
!(con)->constisnull)
-#define fix_scan_list(root, lst, rtoffset) \
- ((List *) fix_scan_expr(root, (Node *) (lst), rtoffset))
+#define fix_scan_list(root, lst, rtoffset, num_exec) \
+ ((List *) fix_scan_expr(root, (Node *) (lst), rtoffset, num_exec))
static void add_rtes_to_flat_rtable(PlannerInfo *root, bool recursing);
static void flatten_unplanned_rtes(PlannerGlobal *glob, RangeTblEntry *rte);
@@ -109,7 +131,8 @@ static Plan *set_mergeappend_references(PlannerInfo *root,
int rtoffset);
static void set_hash_references(PlannerInfo *root, Plan *plan, int rtoffset);
static Relids offset_relid_set(Relids relids, int rtoffset);
-static Node *fix_scan_expr(PlannerInfo *root, Node *node, int rtoffset);
+static Node *fix_scan_expr(PlannerInfo *root, Node *node,
+ int rtoffset, double num_exec);
static Node *fix_scan_expr_mutator(Node *node, fix_scan_expr_context *context);
static bool fix_scan_expr_walker(Node *node, fix_scan_expr_context *context);
static void set_join_references(PlannerInfo *root, Join *join, int rtoffset);
@@ -133,14 +156,15 @@ static List *fix_join_expr(PlannerInfo *root,
List *clauses,
indexed_tlist *outer_itlist,
indexed_tlist *inner_itlist,
- Index acceptable_rel, int rtoffset);
+ Index acceptable_rel,
+ int rtoffset, double num_exec);
static Node *fix_join_expr_mutator(Node *node,
fix_join_expr_context *context);
static Node *fix_upper_expr(PlannerInfo *root,
Node *node,
indexed_tlist *subplan_itlist,
Index newvarno,
- int rtoffset);
+ int rtoffset, double num_exec);
static Node *fix_upper_expr_mutator(Node *node,
fix_upper_expr_context *context);
static List *set_returning_clause_references(PlannerInfo *root,
@@ -177,17 +201,20 @@ static List *set_returning_clause_references(PlannerInfo *root,
* 5. PARAM_MULTIEXPR Params are replaced by regular PARAM_EXEC Params,
* now that we have finished planning all MULTIEXPR subplans.
*
- * 6. We compute regproc OIDs for operators (ie, we look up the function
+ * 6. AlternativeSubPlan expressions are replaced by just one of their
+ * alternatives, using an estimate of how many times they'll be executed.
+ *
+ * 7. We compute regproc OIDs for operators (ie, we look up the function
* that implements each op).
*
- * 7. We create lists of specific objects that the plan depends on.
+ * 8. We create lists of specific objects that the plan depends on.
* This will be used by plancache.c to drive invalidation of cached plans.
* Relation dependencies are represented by OIDs, and everything else by
* PlanInvalItems (this distinction is motivated by the shared-inval APIs).
* Currently, relations, user-defined functions, and domains are the only
* types of objects that are explicitly tracked this way.
*
- * 8. We assign every plan node in the tree a unique ID.
+ * 9. We assign every plan node in the tree a unique ID.
*
* We also perform one final optimization step, which is to delete
* SubqueryScan, Append, and MergeAppend plan nodes that aren't doing
@@ -490,9 +517,11 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
splan->scanrelid += rtoffset;
splan->plan.targetlist =
- fix_scan_list(root, splan->plan.targetlist, rtoffset);
+ fix_scan_list(root, splan->plan.targetlist,
+ rtoffset, NUM_EXEC_TLIST(plan));
splan->plan.qual =
- fix_scan_list(root, splan->plan.qual, rtoffset);
+ fix_scan_list(root, splan->plan.qual,
+ rtoffset, NUM_EXEC_QUAL(plan));
}
break;
case T_SampleScan:
@@ -501,11 +530,14 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
splan->scan.scanrelid += rtoffset;
splan->scan.plan.targetlist =
- fix_scan_list(root, splan->scan.plan.targetlist, rtoffset);
+ fix_scan_list(root, splan->scan.plan.targetlist,
+ rtoffset, NUM_EXEC_TLIST(plan));
splan->scan.plan.qual =
- fix_scan_list(root, splan->scan.plan.qual, rtoffset);
+ fix_scan_list(root, splan->scan.plan.qual,
+ rtoffset, NUM_EXEC_QUAL(plan));
splan->tablesample = (TableSampleClause *)
- fix_scan_expr(root, (Node *) splan->tablesample, rtoffset);
+ fix_scan_expr(root, (Node *) splan->tablesample,
+ rtoffset, 1);
}
break;
case T_IndexScan:
@@ -514,17 +546,23 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
splan->scan.scanrelid += rtoffset;
splan->scan.plan.targetlist =
- fix_scan_list(root, splan->scan.plan.targetlist, rtoffset);
+ fix_scan_list(root, splan->scan.plan.targetlist,
+ rtoffset, NUM_EXEC_TLIST(plan));
splan->scan.plan.qual =
- fix_scan_list(root, splan->scan.plan.qual, rtoffset);
+ fix_scan_list(root, splan->scan.plan.qual,
+ rtoffset, NUM_EXEC_QUAL(plan));
splan->indexqual =
- fix_scan_list(root, splan->indexqual, rtoffset);
+ fix_scan_list(root, splan->indexqual,
+ rtoffset, 1);
splan->indexqualorig =
- fix_scan_list(root, splan->indexqualorig, rtoffset);
+ fix_scan_list(root, splan->indexqualorig,
+ rtoffset, NUM_EXEC_QUAL(plan));
splan->indexorderby =
- fix_scan_list(root, splan->indexorderby, rtoffset);
+ fix_scan_list(root, splan->indexorderby,
+ rtoffset, 1);
splan->indexorderbyorig =
- fix_scan_list(root, splan->indexorderbyorig, rtoffset);
+ fix_scan_list(root, splan->indexorderbyorig,
+ rtoffset, NUM_EXEC_QUAL(plan));
}
break;
case T_IndexOnlyScan:
@@ -543,9 +581,10 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
Assert(splan->scan.plan.targetlist == NIL);
Assert(splan->scan.plan.qual == NIL);
splan->indexqual =
- fix_scan_list(root, splan->indexqual, rtoffset);
+ fix_scan_list(root, splan->indexqual, rtoffset, 1);
splan->indexqualorig =
- fix_scan_list(root, splan->indexqualorig, rtoffset);
+ fix_scan_list(root, splan->indexqualorig,
+ rtoffset, NUM_EXEC_QUAL(plan));
}
break;
case T_BitmapHeapScan:
@@ -554,11 +593,14 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
splan->scan.scanrelid += rtoffset;
splan->scan.plan.targetlist =
- fix_scan_list(root, splan->scan.plan.targetlist, rtoffset);
+ fix_scan_list(root, splan->scan.plan.targetlist,
+ rtoffset, NUM_EXEC_TLIST(plan));
splan->scan.plan.qual =
- fix_scan_list(root, splan->scan.plan.qual, rtoffset);
+ fix_scan_list(root, splan->scan.plan.qual,
+ rtoffset, NUM_EXEC_QUAL(plan));
splan->bitmapqualorig =
- fix_scan_list(root, splan->bitmapqualorig, rtoffset);
+ fix_scan_list(root, splan->bitmapqualorig,
+ rtoffset, NUM_EXEC_QUAL(plan));
}
break;
case T_TidScan:
@@ -567,11 +609,14 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
splan->scan.scanrelid += rtoffset;
splan->scan.plan.targetlist =
- fix_scan_list(root, splan->scan.plan.targetlist, rtoffset);
+ fix_scan_list(root, splan->scan.plan.targetlist,
+ rtoffset, NUM_EXEC_TLIST(plan));
splan->scan.plan.qual =
- fix_scan_list(root, splan->scan.plan.qual, rtoffset);
+ fix_scan_list(root, splan->scan.plan.qual,
+ rtoffset, NUM_EXEC_QUAL(plan));
splan->tidquals =
- fix_scan_list(root, splan->tidquals, rtoffset);
+ fix_scan_list(root, splan->tidquals,
+ rtoffset, 1);
}
break;
case T_SubqueryScan:
@@ -585,11 +630,13 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
splan->scan.scanrelid += rtoffset;
splan->scan.plan.targetlist =
- fix_scan_list(root, splan->scan.plan.targetlist, rtoffset);
+ fix_scan_list(root, splan->scan.plan.targetlist,
+ rtoffset, NUM_EXEC_TLIST(plan));
splan->scan.plan.qual =
- fix_scan_list(root, splan->scan.plan.qual, rtoffset);
+ fix_scan_list(root, splan->scan.plan.qual,
+ rtoffset, NUM_EXEC_QUAL(plan));
splan->functions =
- fix_scan_list(root, splan->functions, rtoffset);
+ fix_scan_list(root, splan->functions, rtoffset, 1);
}
break;
case T_TableFuncScan:
@@ -598,11 +645,14 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
splan->scan.scanrelid += rtoffset;
splan->scan.plan.targetlist =
- fix_scan_list(root, splan->scan.plan.targetlist, rtoffset);
+ fix_scan_list(root, splan->scan.plan.targetlist,
+ rtoffset, NUM_EXEC_TLIST(plan));
splan->scan.plan.qual =
- fix_scan_list(root, splan->scan.plan.qual, rtoffset);
+ fix_scan_list(root, splan->scan.plan.qual,
+ rtoffset, NUM_EXEC_QUAL(plan));
splan->tablefunc = (TableFunc *)
- fix_scan_expr(root, (Node *) splan->tablefunc, rtoffset);
+ fix_scan_expr(root, (Node *) splan->tablefunc,
+ rtoffset, 1);
}
break;
case T_ValuesScan:
@@ -611,11 +661,14 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
splan->scan.scanrelid += rtoffset;
splan->scan.plan.targetlist =
- fix_scan_list(root, splan->scan.plan.targetlist, rtoffset);
+ fix_scan_list(root, splan->scan.plan.targetlist,
+ rtoffset, NUM_EXEC_TLIST(plan));
splan->scan.plan.qual =
- fix_scan_list(root, splan->scan.plan.qual, rtoffset);
+ fix_scan_list(root, splan->scan.plan.qual,
+ rtoffset, NUM_EXEC_QUAL(plan));
splan->values_lists =
- fix_scan_list(root, splan->values_lists, rtoffset);
+ fix_scan_list(root, splan->values_lists,
+ rtoffset, 1);
}
break;
case T_CteScan:
@@ -624,9 +677,11 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
splan->scan.scanrelid += rtoffset;
splan->scan.plan.targetlist =
- fix_scan_list(root, splan->scan.plan.targetlist, rtoffset);
+ fix_scan_list(root, splan->scan.plan.targetlist,
+ rtoffset, NUM_EXEC_TLIST(plan));
splan->scan.plan.qual =
- fix_scan_list(root, splan->scan.plan.qual, rtoffset);
+ fix_scan_list(root, splan->scan.plan.qual,
+ rtoffset, NUM_EXEC_QUAL(plan));
}
break;
case T_NamedTuplestoreScan:
@@ -635,9 +690,11 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
splan->scan.scanrelid += rtoffset;
splan->scan.plan.targetlist =
- fix_scan_list(root, splan->scan.plan.targetlist, rtoffset);
+ fix_scan_list(root, splan->scan.plan.targetlist,
+ rtoffset, NUM_EXEC_TLIST(plan));
splan->scan.plan.qual =
- fix_scan_list(root, splan->scan.plan.qual, rtoffset);
+ fix_scan_list(root, splan->scan.plan.qual,
+ rtoffset, NUM_EXEC_QUAL(plan));
}
break;
case T_WorkTableScan:
@@ -646,9 +703,11 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
splan->scan.scanrelid += rtoffset;
splan->scan.plan.targetlist =
- fix_scan_list(root, splan->scan.plan.targetlist, rtoffset);
+ fix_scan_list(root, splan->scan.plan.targetlist,
+ rtoffset, NUM_EXEC_TLIST(plan));
splan->scan.plan.qual =
- fix_scan_list(root, splan->scan.plan.qual, rtoffset);
+ fix_scan_list(root, splan->scan.plan.qual,
+ rtoffset, NUM_EXEC_QUAL(plan));
}
break;
case T_ForeignScan:
@@ -732,9 +791,9 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
Assert(splan->plan.qual == NIL);
splan->limitOffset =
- fix_scan_expr(root, splan->limitOffset, rtoffset);
+ fix_scan_expr(root, splan->limitOffset, rtoffset, 1);
splan->limitCount =
- fix_scan_expr(root, splan->limitCount, rtoffset);
+ fix_scan_expr(root, splan->limitCount, rtoffset, 1);
}
break;
case T_Agg:
@@ -775,9 +834,9 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
* variable refs, so fix_scan_expr works for them.
*/
wplan->startOffset =
- fix_scan_expr(root, wplan->startOffset, rtoffset);
+ fix_scan_expr(root, wplan->startOffset, rtoffset, 1);
wplan->endOffset =
- fix_scan_expr(root, wplan->endOffset, rtoffset);
+ fix_scan_expr(root, wplan->endOffset, rtoffset, 1);
}
break;
case T_Result:
@@ -793,13 +852,15 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
else
{
splan->plan.targetlist =
- fix_scan_list(root, splan->plan.targetlist, rtoffset);
+ fix_scan_list(root, splan->plan.targetlist,
+ rtoffset, NUM_EXEC_TLIST(plan));
splan->plan.qual =
- fix_scan_list(root, splan->plan.qual, rtoffset);
+ fix_scan_list(root, splan->plan.qual,
+ rtoffset, NUM_EXEC_QUAL(plan));
}
/* resconstantqual can't contain any subplan variable refs */
splan->resconstantqual =
- fix_scan_expr(root, splan->resconstantqual, rtoffset);
+ fix_scan_expr(root, splan->resconstantqual, rtoffset, 1);
}
break;
case T_ProjectSet:
@@ -813,7 +874,8 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
Assert(splan->plan.qual == NIL);
splan->withCheckOptionLists =
- fix_scan_list(root, splan->withCheckOptionLists, rtoffset);
+ fix_scan_list(root, splan->withCheckOptionLists,
+ rtoffset, 1);
if (splan->returningLists)
{
@@ -874,18 +936,18 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
fix_join_expr(root, splan->onConflictSet,
NULL, itlist,
linitial_int(splan->resultRelations),
- rtoffset);
+ rtoffset, NUM_EXEC_QUAL(plan));
splan->onConflictWhere = (Node *)
fix_join_expr(root, (List *) splan->onConflictWhere,
NULL, itlist,
linitial_int(splan->resultRelations),
- rtoffset);
+ rtoffset, NUM_EXEC_QUAL(plan));
pfree(itlist);
splan->exclRelTlist =
- fix_scan_list(root, splan->exclRelTlist, rtoffset);
+ fix_scan_list(root, splan->exclRelTlist, rtoffset, 1);
}
splan->nominalRelation += rtoffset;
@@ -1026,19 +1088,24 @@ set_indexonlyscan_references(PlannerInfo *root,
(Node *) plan->scan.plan.targetlist,
index_itlist,
INDEX_VAR,
- rtoffset);
+ rtoffset,
+ NUM_EXEC_TLIST((Plan *) plan));
plan->scan.plan.qual = (List *)
fix_upper_expr(root,
(Node *) plan->scan.plan.qual,
index_itlist,
INDEX_VAR,
- rtoffset);
+ rtoffset,
+ NUM_EXEC_QUAL((Plan *) plan));
/* indexqual is already transformed to reference index columns */
- plan->indexqual = fix_scan_list(root, plan->indexqual, rtoffset);
+ plan->indexqual = fix_scan_list(root, plan->indexqual,
+ rtoffset, 1);
/* indexorderby is already transformed to reference index columns */
- plan->indexorderby = fix_scan_list(root, plan->indexorderby, rtoffset);
+ plan->indexorderby = fix_scan_list(root, plan->indexorderby,
+ rtoffset, 1);
/* indextlist must NOT be transformed to reference index columns */
- plan->indextlist = fix_scan_list(root, plan->indextlist, rtoffset);
+ plan->indextlist = fix_scan_list(root, plan->indextlist,
+ rtoffset, NUM_EXEC_TLIST((Plan *) plan));
pfree(index_itlist);
@@ -1084,9 +1151,11 @@ set_subqueryscan_references(PlannerInfo *root,
*/
plan->scan.scanrelid += rtoffset;
plan->scan.plan.targetlist =
- fix_scan_list(root, plan->scan.plan.targetlist, rtoffset);
+ fix_scan_list(root, plan->scan.plan.targetlist,
+ rtoffset, NUM_EXEC_TLIST((Plan *) plan));
plan->scan.plan.qual =
- fix_scan_list(root, plan->scan.plan.qual, rtoffset);
+ fix_scan_list(root, plan->scan.plan.qual,
+ rtoffset, NUM_EXEC_QUAL((Plan *) plan));
result = (Plan *) plan;
}
@@ -1202,29 +1271,34 @@ set_foreignscan_references(PlannerInfo *root,
(Node *) fscan->scan.plan.targetlist,
itlist,
INDEX_VAR,
- rtoffset);
+ rtoffset,
+ NUM_EXEC_TLIST((Plan *) fscan));
fscan->scan.plan.qual = (List *)
fix_upper_expr(root,
(Node *) fscan->scan.plan.qual,
itlist,
INDEX_VAR,
- rtoffset);
+ rtoffset,
+ NUM_EXEC_QUAL((Plan *) fscan));
fscan->fdw_exprs = (List *)
fix_upper_expr(root,
(Node *) fscan->fdw_exprs,
itlist,
INDEX_VAR,
- rtoffset);
+ rtoffset,
+ NUM_EXEC_QUAL((Plan *) fscan));
fscan->fdw_recheck_quals = (List *)
fix_upper_expr(root,
(Node *) fscan->fdw_recheck_quals,
itlist,
INDEX_VAR,
- rtoffset);
+ rtoffset,
+ NUM_EXEC_QUAL((Plan *) fscan));
pfree(itlist);
/* fdw_scan_tlist itself just needs fix_scan_list() adjustments */
fscan->fdw_scan_tlist =
- fix_scan_list(root, fscan->fdw_scan_tlist, rtoffset);
+ fix_scan_list(root, fscan->fdw_scan_tlist,
+ rtoffset, NUM_EXEC_TLIST((Plan *) fscan));
}
else
{
@@ -1233,13 +1307,17 @@ set_foreignscan_references(PlannerInfo *root,
* way
*/
fscan->scan.plan.targetlist =
- fix_scan_list(root, fscan->scan.plan.targetlist, rtoffset);
+ fix_scan_list(root, fscan->scan.plan.targetlist,
+ rtoffset, NUM_EXEC_TLIST((Plan *) fscan));
fscan->scan.plan.qual =
- fix_scan_list(root, fscan->scan.plan.qual, rtoffset);
+ fix_scan_list(root, fscan->scan.plan.qual,
+ rtoffset, NUM_EXEC_QUAL((Plan *) fscan));
fscan->fdw_exprs =
- fix_scan_list(root, fscan->fdw_exprs, rtoffset);
+ fix_scan_list(root, fscan->fdw_exprs,
+ rtoffset, NUM_EXEC_QUAL((Plan *) fscan));
fscan->fdw_recheck_quals =
- fix_scan_list(root, fscan->fdw_recheck_quals, rtoffset);
+ fix_scan_list(root, fscan->fdw_recheck_quals,
+ rtoffset, NUM_EXEC_QUAL((Plan *) fscan));
}
fscan->fs_relids = offset_relid_set(fscan->fs_relids, rtoffset);
@@ -1270,33 +1348,40 @@ set_customscan_references(PlannerInfo *root,
(Node *) cscan->scan.plan.targetlist,
itlist,
INDEX_VAR,
- rtoffset);
+ rtoffset,
+ NUM_EXEC_TLIST((Plan *) cscan));
cscan->scan.plan.qual = (List *)
fix_upper_expr(root,
(Node *) cscan->scan.plan.qual,
itlist,
INDEX_VAR,
- rtoffset);
+ rtoffset,
+ NUM_EXEC_QUAL((Plan *) cscan));
cscan->custom_exprs = (List *)
fix_upper_expr(root,
(Node *) cscan->custom_exprs,
itlist,
INDEX_VAR,
- rtoffset);
+ rtoffset,
+ NUM_EXEC_QUAL((Plan *) cscan));
pfree(itlist);
/* custom_scan_tlist itself just needs fix_scan_list() adjustments */
cscan->custom_scan_tlist =
- fix_scan_list(root, cscan->custom_scan_tlist, rtoffset);
+ fix_scan_list(root, cscan->custom_scan_tlist,
+ rtoffset, NUM_EXEC_TLIST((Plan *) cscan));
}
else
{
/* Adjust tlist, qual, custom_exprs in the standard way */
cscan->scan.plan.targetlist =
- fix_scan_list(root, cscan->scan.plan.targetlist, rtoffset);
+ fix_scan_list(root, cscan->scan.plan.targetlist,
+ rtoffset, NUM_EXEC_TLIST((Plan *) cscan));
cscan->scan.plan.qual =
- fix_scan_list(root, cscan->scan.plan.qual, rtoffset);
+ fix_scan_list(root, cscan->scan.plan.qual,
+ rtoffset, NUM_EXEC_QUAL((Plan *) cscan));
cscan->custom_exprs =
- fix_scan_list(root, cscan->custom_exprs, rtoffset);
+ fix_scan_list(root, cscan->custom_exprs,
+ rtoffset, NUM_EXEC_QUAL((Plan *) cscan));
}
/* Adjust child plan-nodes recursively, if needed */
@@ -1458,7 +1543,8 @@ set_hash_references(PlannerInfo *root, Plan *plan, int rtoffset)
(Node *) hplan->hashkeys,
outer_itlist,
OUTER_VAR,
- rtoffset);
+ rtoffset,
+ NUM_EXEC_QUAL(plan));
/* Hash doesn't project */
set_dummy_tlist_references(plan, rtoffset);
@@ -1624,27 +1710,93 @@ fix_param_node(PlannerInfo *root, Param *p)
}
/*
+ * fix_alternative_subplan
+ * Do set_plan_references processing on an AlternativeSubPlan
+ *
+ * Choose one of the alternative implementations and return just that one,
+ * discarding the rest of the AlternativeSubPlan structure.
+ * Note: caller must still recurse into the result!
+ *
+ * We don't make any attempt to fix up cost estimates in the parent plan
+ * node or higher-level nodes. However, we do remove the rejected subplan(s)
+ * from root->glob->subplans, to minimize cycles expended on them later.
+ */
+static Node *
+fix_alternative_subplan(PlannerInfo *root, AlternativeSubPlan *asplan,
+ double num_exec)
+{
+ SubPlan *bestplan = NULL;
+ Cost bestcost = 0;
+ ListCell *lc;
+
+ /*
+ * Compute the estimated cost of each subplan assuming num_exec
+ * executions, and keep the cheapest one. Replace discarded subplans with
+ * NULL pointers in the global subplans list. In event of exact equality
+ * of estimates, we prefer the later plan; this is a bit arbitrary, but in
+ * current usage it biases us to break ties against fast-start subplans.
+ */
+ Assert(asplan->subplans != NIL);
+
+ foreach(lc, asplan->subplans)
+ {
+ SubPlan *curplan = (SubPlan *) lfirst(lc);
+ Cost curcost;
+
+ curcost = curplan->startup_cost + num_exec * curplan->per_call_cost;
+ if (bestplan == NULL)
+ {
+ bestplan = curplan;
+ bestcost = curcost;
+ }
+ else if (curcost <= bestcost)
+ {
+ /* drop old bestplan */
+ ListCell *lc2 = list_nth_cell(root->glob->subplans,
+ bestplan->plan_id - 1);
+
+ lfirst(lc2) = NULL;
+ bestplan = curplan;
+ bestcost = curcost;
+ }
+ else
+ {
+ /* drop curplan */
+ ListCell *lc2 = list_nth_cell(root->glob->subplans,
+ curplan->plan_id - 1);
+
+ lfirst(lc2) = NULL;
+ }
+ }
+
+ return (Node *) bestplan;
+}
+
+/*
* fix_scan_expr
* Do set_plan_references processing on a scan-level expression
*
* This consists of incrementing all Vars' varnos by rtoffset,
* replacing PARAM_MULTIEXPR Params, expanding PlaceHolderVars,
* replacing Aggref nodes that should be replaced by initplan output Params,
+ * choosing the best implementation for AlternativeSubPlans,
* looking up operator opcode info for OpExpr and related nodes,
* and adding OIDs from regclass Const nodes into root->glob->relationOids.
*/
static Node *
-fix_scan_expr(PlannerInfo *root, Node *node, int rtoffset)
+fix_scan_expr(PlannerInfo *root, Node *node, int rtoffset, double num_exec)
{
fix_scan_expr_context context;
context.root = root;
context.rtoffset = rtoffset;
+ context.num_exec = num_exec;
if (rtoffset != 0 ||
root->multiexpr_params != NIL ||
root->glob->lastPHId != 0 ||
- root->minmax_aggs != NIL)
+ root->minmax_aggs != NIL ||
+ root->hasAlternativeSubPlans)
{
return fix_scan_expr_mutator(node, &context);
}
@@ -1655,7 +1807,8 @@ fix_scan_expr(PlannerInfo *root, Node *node, int rtoffset)
* are no MULTIEXPR subqueries then we don't need to replace
* PARAM_MULTIEXPR Params, and if there are no placeholders anywhere
* we won't need to remove them, and if there are no minmax Aggrefs we
- * won't need to replace them. Then it's OK to just scribble on the
+ * won't need to replace them, and if there are no AlternativeSubPlans
+ * we won't need to remove them. Then it's OK to just scribble on the
* input node tree instead of copying (since the only change, filling
* in any unset opfuncid fields, is harmless). This saves just enough
* cycles to be noticeable on trivial queries.
@@ -1729,6 +1882,11 @@ fix_scan_expr_mutator(Node *node, fix_scan_expr_context *context)
return fix_scan_expr_mutator((Node *) phv->phexpr, context);
}
+ if (IsA(node, AlternativeSubPlan))
+ return fix_scan_expr_mutator(fix_alternative_subplan(context->root,
+ (AlternativeSubPlan *) node,
+ context->num_exec),
+ context);
fix_expr_common(context->root, node);
return expression_tree_mutator(node, fix_scan_expr_mutator,
(void *) context);
@@ -1740,6 +1898,7 @@ fix_scan_expr_walker(Node *node, fix_scan_expr_context *context)
if (node == NULL)
return false;
Assert(!IsA(node, PlaceHolderVar));
+ Assert(!IsA(node, AlternativeSubPlan));
fix_expr_common(context->root, node);
return expression_tree_walker(node, fix_scan_expr_walker,
(void *) context);
@@ -1776,7 +1935,8 @@ set_join_references(PlannerInfo *root, Join *join, int rtoffset)
outer_itlist,
inner_itlist,
(Index) 0,
- rtoffset);
+ rtoffset,
+ NUM_EXEC_QUAL((Plan *) join));
/* Now do join-type-specific stuff */
if (IsA(join, NestLoop))
@@ -1792,7 +1952,8 @@ set_join_references(PlannerInfo *root, Join *join, int rtoffset)
(Node *) nlp->paramval,
outer_itlist,
OUTER_VAR,
- rtoffset);
+ rtoffset,
+ NUM_EXEC_TLIST(outer_plan));
/* Check we replaced any PlaceHolderVar with simple Var */
if (!(IsA(nlp->paramval, Var) &&
nlp->paramval->varno == OUTER_VAR))
@@ -1808,7 +1969,8 @@ set_join_references(PlannerInfo *root, Join *join, int rtoffset)
outer_itlist,
inner_itlist,
(Index) 0,
- rtoffset);
+ rtoffset,
+ NUM_EXEC_QUAL((Plan *) join));
}
else if (IsA(join, HashJoin))
{
@@ -1819,7 +1981,8 @@ set_join_references(PlannerInfo *root, Join *join, int rtoffset)
outer_itlist,
inner_itlist,
(Index) 0,
- rtoffset);
+ rtoffset,
+ NUM_EXEC_QUAL((Plan *) join));
/*
* HashJoin's hashkeys are used to look for matching tuples from its
@@ -1829,7 +1992,8 @@ set_join_references(PlannerInfo *root, Join *join, int rtoffset)
(Node *) hj->hashkeys,
outer_itlist,
OUTER_VAR,
- rtoffset);
+ rtoffset,
+ NUM_EXEC_QUAL((Plan *) join));
}
/*
@@ -1867,13 +2031,15 @@ set_join_references(PlannerInfo *root, Join *join, int rtoffset)
outer_itlist,
inner_itlist,
(Index) 0,
- rtoffset);
+ rtoffset,
+ NUM_EXEC_TLIST((Plan *) join));
join->plan.qual = fix_join_expr(root,
join->plan.qual,
outer_itlist,
inner_itlist,
(Index) 0,
- rtoffset);
+ rtoffset,
+ NUM_EXEC_QUAL((Plan *) join));
pfree(outer_itlist);
pfree(inner_itlist);
@@ -1926,14 +2092,16 @@ set_upper_references(PlannerInfo *root, Plan *plan, int rtoffset)
(Node *) tle->expr,
subplan_itlist,
OUTER_VAR,
- rtoffset);
+ rtoffset,
+ NUM_EXEC_TLIST(plan));
}
else
newexpr = fix_upper_expr(root,
(Node *) tle->expr,
subplan_itlist,
OUTER_VAR,
- rtoffset);
+ rtoffset,
+ NUM_EXEC_TLIST(plan));
tle = flatCopyTargetEntry(tle);
tle->expr = (Expr *) newexpr;
output_targetlist = lappend(output_targetlist, tle);
@@ -1945,7 +2113,8 @@ set_upper_references(PlannerInfo *root, Plan *plan, int rtoffset)
(Node *) plan->qual,
subplan_itlist,
OUTER_VAR,
- rtoffset);
+ rtoffset,
+ NUM_EXEC_QUAL(plan));
pfree(subplan_itlist);
}
@@ -2389,6 +2558,7 @@ search_indexed_tlist_for_sortgroupref(Expr *node,
* 'acceptable_rel' is either zero or the rangetable index of a relation
* whose Vars may appear in the clause without provoking an error
* 'rtoffset': how much to increment varnos by
+ * 'num_exec': estimated number of executions of expression
*
* Returns the new expression tree. The original clause structure is
* not modified.
@@ -2399,7 +2569,8 @@ fix_join_expr(PlannerInfo *root,
indexed_tlist *outer_itlist,
indexed_tlist *inner_itlist,
Index acceptable_rel,
- int rtoffset)
+ int rtoffset,
+ double num_exec)
{
fix_join_expr_context context;
@@ -2408,6 +2579,7 @@ fix_join_expr(PlannerInfo *root,
context.inner_itlist = inner_itlist;
context.acceptable_rel = acceptable_rel;
context.rtoffset = rtoffset;
+ context.num_exec = num_exec;
return (List *) fix_join_expr_mutator((Node *) clauses, &context);
}
@@ -2502,6 +2674,11 @@ fix_join_expr_mutator(Node *node, fix_join_expr_context *context)
/* Special cases (apply only AFTER failing to match to lower tlist) */
if (IsA(node, Param))
return fix_param_node(context->root, (Param *) node);
+ if (IsA(node, AlternativeSubPlan))
+ return fix_join_expr_mutator(fix_alternative_subplan(context->root,
+ (AlternativeSubPlan *) node,
+ context->num_exec),
+ context);
fix_expr_common(context->root, node);
return expression_tree_mutator(node,
fix_join_expr_mutator,
@@ -2533,6 +2710,7 @@ fix_join_expr_mutator(Node *node, fix_join_expr_context *context)
* 'subplan_itlist': indexed target list for subplan (or index)
* 'newvarno': varno to use for Vars referencing tlist elements
* 'rtoffset': how much to increment varnos by
+ * 'num_exec': estimated number of executions of expression
*
* The resulting tree is a copy of the original in which all Var nodes have
* varno = newvarno, varattno = resno of corresponding targetlist element.
@@ -2543,7 +2721,8 @@ fix_upper_expr(PlannerInfo *root,
Node *node,
indexed_tlist *subplan_itlist,
Index newvarno,
- int rtoffset)
+ int rtoffset,
+ double num_exec)
{
fix_upper_expr_context context;
@@ -2551,6 +2730,7 @@ fix_upper_expr(PlannerInfo *root,
context.subplan_itlist = subplan_itlist;
context.newvarno = newvarno;
context.rtoffset = rtoffset;
+ context.num_exec = num_exec;
return fix_upper_expr_mutator(node, &context);
}
@@ -2623,6 +2803,11 @@ fix_upper_expr_mutator(Node *node, fix_upper_expr_context *context)
}
/* If no match, just fall through to process it normally */
}
+ if (IsA(node, AlternativeSubPlan))
+ return fix_upper_expr_mutator(fix_alternative_subplan(context->root,
+ (AlternativeSubPlan *) node,
+ context->num_exec),
+ context);
fix_expr_common(context->root, node);
return expression_tree_mutator(node,
fix_upper_expr_mutator,
@@ -2687,7 +2872,8 @@ set_returning_clause_references(PlannerInfo *root,
itlist,
NULL,
resultRelation,
- rtoffset);
+ rtoffset,
+ NUM_EXEC_TLIST(topplan));
pfree(itlist);
diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c
index 6eb794669fe..fcce81926b7 100644
--- a/src/backend/optimizer/plan/subselect.c
+++ b/src/backend/optimizer/plan/subselect.c
@@ -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)
@@ -736,6 +741,31 @@ subplan_is_hashable(Plan *plan)
}
/*
+ * 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?
*
* To identify LHS vs RHS of the hash expression, we must be given the
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 15877e37a60..03cf2419963 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -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)
{
diff --git a/src/include/executor/execExpr.h b/src/include/executor/execExpr.h
index dbe8649a576..b792de1bc95 100644
--- a/src/include/executor/execExpr.h
+++ b/src/include/executor/execExpr.h
@@ -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,
diff --git a/src/include/executor/nodeSubplan.h b/src/include/executor/nodeSubplan.h
index 83e90b3d07b..b629af1f5fb 100644
--- a/src/include/executor/nodeSubplan.h
+++ b/src/include/executor/nodeSubplan.h
@@ -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);
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index a5ab1aed14d..ef448d67c77 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -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
*
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 381d84b4e4f..7ddd8c011bf 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -213,7 +213,6 @@ typedef enum NodeTag
T_WindowFuncExprState,
T_SetExprState,
T_SubPlanState,
- T_AlternativeSubPlanState,
T_DomainConstraintState,
/*
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index 485d1b06c91..dbe86e7af65 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -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: */
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index d73be2ad46c..fd65ee8f9c5 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -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
diff --git a/src/test/regress/expected/insert_conflict.out b/src/test/regress/expected/insert_conflict.out
index 1338b2b23e1..ff157ceb1c1 100644
--- a/src/test/regress/expected/insert_conflict.out
+++ b/src/test/regress/expected/insert_conflict.out
@@ -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
diff --git a/src/test/regress/expected/subselect.out b/src/test/regress/expected/subselect.out
index b81923f2e74..9d56cdacf37 100644
--- a/src/test/regress/expected/subselect.out
+++ b/src/test/regress/expected/subselect.out
@@ -875,6 +875,53 @@ 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);
+ 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
--
select a.thousand from tenk1 a, tenk1 b
diff --git a/src/test/regress/expected/updatable_views.out b/src/test/regress/expected/updatable_views.out
index 5de53f2782a..caed1c19ec7 100644
--- a/src/test/regress/expected/updatable_views.out
+++ b/src/test/regress/expected/updatable_views.out
@@ -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
diff --git a/src/test/regress/sql/subselect.sql b/src/test/regress/sql/subselect.sql
index cce8ebdb3d9..a25cb6fc5c5 100644
--- a/src/test/regress/sql/subselect.sql
+++ b/src/test/regress/sql/subselect.sql
@@ -510,6 +510,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
--
select a.thousand from tenk1 a, tenk1 b