diff --git a/contrib/file_fdw/file_fdw.c b/contrib/file_fdw/file_fdw.c index 4773cadec0..ba52b48783 100644 --- a/contrib/file_fdw/file_fdw.c +++ b/contrib/file_fdw/file_fdw.c @@ -129,7 +129,8 @@ static ForeignScan *fileGetForeignPlan(PlannerInfo *root, ForeignPath *best_path, List *tlist, List *scan_clauses, - Plan *outer_plan); + Plan *outer_plan, + double est_calls); static void fileExplainForeignScan(ForeignScanState *node, ExplainState *es); static void fileBeginForeignScan(ForeignScanState *node, int eflags); static TupleTableSlot *fileIterateForeignScan(ForeignScanState *node); @@ -588,7 +589,8 @@ fileGetForeignPlan(PlannerInfo *root, ForeignPath *best_path, List *tlist, List *scan_clauses, - Plan *outer_plan) + Plan *outer_plan, + double est_calls) { Index scan_relid = baserel->relid; diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c index 0e5771c89d..f51bf8a649 100644 --- a/contrib/postgres_fdw/postgres_fdw.c +++ b/contrib/postgres_fdw/postgres_fdw.c @@ -335,7 +335,8 @@ static ForeignScan *postgresGetForeignPlan(PlannerInfo *root, ForeignPath *best_path, List *tlist, List *scan_clauses, - Plan *outer_plan); + Plan *outer_plan, + double est_calls); static void postgresBeginForeignScan(ForeignScanState *node, int eflags); static TupleTableSlot *postgresIterateForeignScan(ForeignScanState *node); static void postgresReScanForeignScan(ForeignScanState *node); @@ -1221,7 +1222,8 @@ postgresGetForeignPlan(PlannerInfo *root, ForeignPath *best_path, List *tlist, List *scan_clauses, - Plan *outer_plan) + Plan *outer_plan, + double est_calls) { PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) foreignrel->fdw_private; Index scan_relid; @@ -1385,7 +1387,8 @@ postgresGetForeignPlan(PlannerInfo *root, * a Result node atop the plan tree. */ outer_plan = change_plan_targetlist(outer_plan, fdw_scan_tlist, - best_path->path.parallel_safe); + best_path->path.parallel_safe, + est_calls); } } diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index 03986946a8..643680bbc6 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -5603,8 +5603,11 @@ ANY num_sync ( ). + Sets the cost threshold for which a given plan node will consider + performing JIT compilation for itself. The total_cost + of a given plan node multiplied by its estimated loops + must exceed this value before JIT compilation is considered for the + plan node xref linkend="jit"/> must also be enabled. Performing JIT costs planning time but can accelerate query execution. Setting this to -1 disables JIT compilation. @@ -5621,12 +5624,13 @@ ANY num_sync ( num_sync ( startup_cost, plan->total_cost, - plan->plan_rows, plan->plan_width); + /* only display the expected loops if it's above 1.0 */ + if (plan->est_calls <= 1.0) + appendStringInfo(es->str, " (cost=%.2f..%.2f rows=%.0f width=%d)", + plan->startup_cost, plan->total_cost, + plan->plan_rows, plan->plan_width); + else + appendStringInfo(es->str, " (cost=%.2f..%.2f rows=%.0f width=%d loops=%.0f)", + plan->startup_cost, plan->total_cost, + plan->plan_rows, plan->plan_width, + plan->est_calls); + } else { @@ -1597,6 +1605,7 @@ ExplainNode(PlanState *planstate, List *ancestors, 0, es); ExplainPropertyInteger("Plan Width", NULL, plan->plan_width, es); + ExplainPropertyFloat("Plan Calls", NULL, plan->est_calls, 0, es); } } @@ -1719,6 +1728,21 @@ ExplainNode(PlanState *planstate, List *ancestors, if (es->verbose) show_plan_tlist(planstate, ancestors, es); + if (es->format == EXPLAIN_FORMAT_TEXT) + { + /* + * If we did any jitting, indicate if we did any for this node or not. + * When format is TEXT, only do this when VERBOSE is enabled. + */ + if (planstate->state->es_jit != NULL && es->verbose) + ExplainPropertyBool("JIT", plan->jit, es); + } + else + { + if (planstate->state->es_jit != NULL) + ExplainPropertyBool("JIT", plan->jit, es); + } + /* unique join */ switch (nodeTag(plan)) { diff --git a/src/backend/executor/nodeValuesscan.c b/src/backend/executor/nodeValuesscan.c index dda1c59b23..4854b16cfe 100644 --- a/src/backend/executor/nodeValuesscan.c +++ b/src/backend/executor/nodeValuesscan.c @@ -296,22 +296,8 @@ ExecInitValuesScan(ValuesScan *node, EState *estate, int eflags) if (estate->es_subplanstates && contain_subplans((Node *) exprs)) { - int saved_jit_flags; - - /* - * As these expressions are only used once, disable JIT for them. - * This is worthwhile because it's common to insert significant - * amounts of data via VALUES(). Note that this doesn't prevent - * use of JIT *within* a subplan, since that's initialized - * separately; this just affects the upper-level subexpressions. - */ - saved_jit_flags = estate->es_jit_flags; - estate->es_jit_flags = PGJIT_NONE; - scanstate->exprstatelists[i] = ExecInitExprList(exprs, &scanstate->ss.ps); - - estate->es_jit_flags = saved_jit_flags; } i++; } diff --git a/src/backend/jit/README b/src/backend/jit/README index 5427bdf215..a4aa33b79b 100644 --- a/src/backend/jit/README +++ b/src/backend/jit/README @@ -266,27 +266,32 @@ generation, and later compiling larger parts of queries. When to JIT =========== -Currently there are a number of GUCs that influence JITing: - -- jit_above_cost = -1, 0-DBL_MAX - all queries with a higher total cost - get JITed, *without* optimization (expensive part), corresponding to - -O0. This commonly already results in significant speedups if - expression/deforming is a bottleneck (removing dynamic branches - mostly). -- jit_optimize_above_cost = -1, 0-DBL_MAX - all queries with a higher total cost - get JITed, *with* optimization (expensive part). -- jit_inline_above_cost = -1, 0-DBL_MAX - inlining is tried if query has - higher cost. - -Whenever a query's total cost is above these limits, JITing is -performed. - -Alternative costing models, e.g. by generating separate paths for -parts of a query with lower cpu_* costs, are also a possibility, but -it's doubtful the overhead of doing so is sufficient. Another -alternative would be to count the number of times individual +Currently, there are a number of GUCs that influence JITing. Each of these +GUCs defines the cost that a given plan node must exceed to enable the given +JIT feature. The costs here are calculated by multiplying the total_cost of +the plan node by the estimated number of rescans the planner exepects the +executor to perform on the given node. We refer to this cost as the "overall" +cost in the text below: + +- jit_above_cost = -1, 0-DBL_MAX - all plan nodes which have a overall cost + higher than this value get JITed, *without* optimization (expensive part), + corresponding to -O0. This commonly already results in significant speedups + if expression/deforming is a bottleneck (removing dynamic branches mostly). +- jit_optimize_above_cost = -1, 0-DBL_MAX - perform an optimization pass + during JIT compilation when any plan node that is eligible for JIT reaches + this cost. +- jit_inline_above_cost = -1, 0-DBL_MAX - inlining is tried during JIT + compilation if any plan node has a higher overall cost than this value. + +It is important to remember that the optimize and inlining options are either +enabled for all JIT enabled plan nodes or disabled. If a single node reaches +the required threshold then all JIT enabled nodes will be JITed using the same +compilation options. + +An alternative would be to count the number of times individual expressions are estimated to be evaluated, and perform JITing of these -individual expressions. +individual expressions. This is probably more complexity than it would be +worth. The obvious seeming approach of JITing expressions individually after a number of execution turns out not to work too well. Primarily diff --git a/src/backend/jit/jit.c b/src/backend/jit/jit.c index 18d168f1af..ade689bf22 100644 --- a/src/backend/jit/jit.c +++ b/src/backend/jit/jit.c @@ -172,6 +172,14 @@ jit_compile_expr(struct ExprState *state) if (!(state->parent->state->es_jit_flags & PGJIT_EXPR)) return false; + /* don't jit if the plan node is missing */ + if (state->parent->plan == NULL) + return false; + + /* don't jit if it's not enabled for this plan node */ + if (!state->parent->plan->jit) + return false; + /* this also takes !jit_enabled into account */ if (provider_init()) return provider.compile_expr(state); diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index 836f427ea8..e56db41047 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -128,6 +128,7 @@ CopyPlanFields(const Plan *from, Plan *newnode) COPY_SCALAR_FIELD(parallel_aware); COPY_SCALAR_FIELD(parallel_safe); COPY_SCALAR_FIELD(async_capable); + COPY_SCALAR_FIELD(jit); COPY_SCALAR_FIELD(plan_node_id); COPY_NODE_FIELD(targetlist); COPY_NODE_FIELD(qual); diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index 6a02f81ad5..27360d587c 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -341,6 +341,7 @@ _outPlanInfo(StringInfo str, const Plan *node) WRITE_BOOL_FIELD(parallel_aware); WRITE_BOOL_FIELD(parallel_safe); WRITE_BOOL_FIELD(async_capable); + WRITE_BOOL_FIELD(jit); WRITE_INT_FIELD(plan_node_id); WRITE_NODE_FIELD(targetlist); WRITE_NODE_FIELD(qual); @@ -2117,6 +2118,7 @@ _outMemoizePath(StringInfo str, const MemoizePath *node) WRITE_BOOL_FIELD(binary_mode); WRITE_FLOAT_FIELD(calls, "%.0f"); WRITE_UINT_FIELD(est_entries); + WRITE_FLOAT_FIELD(est_hitratio, "%.6f"); } static void diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c index ddf76ac778..137ffbcdd1 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -1847,6 +1847,7 @@ ReadCommonPlan(Plan *local_node) READ_BOOL_FIELD(parallel_aware); READ_BOOL_FIELD(parallel_safe); READ_BOOL_FIELD(async_capable); + READ_BOOL_FIELD(jit); READ_INT_FIELD(plan_node_id); READ_NODE_FIELD(targetlist); READ_NODE_FIELD(qual); diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c index b787c6f81a..c6cf109392 100644 --- a/src/backend/optimizer/path/costsize.c +++ b/src/backend/optimizer/path/costsize.c @@ -2814,6 +2814,12 @@ cost_memoize_rescan(PlannerInfo *root, MemoizePath *mpath, /* Ensure we don't go negative */ hit_ratio = Max(hit_ratio, 0.0); + /* + * Since we've just gone to the bother of calculating the estimated hit + * ratio, let's store that in the MemoizePath for later use. + */ + mpath->est_hitratio = hit_ratio; + /* * Set the total_cost accounting for the expected cache hit ratio. We * also add on a cpu_operator_cost to account for a cache lookup. This diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c index 7905bc4654..9c51103df1 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -22,6 +22,7 @@ #include "access/sysattr.h" #include "catalog/pg_class.h" #include "foreign/fdwapi.h" +#include "jit/jit.h" #include "miscadmin.h" #include "nodes/extensible.h" #include "nodes/makefuncs.h" @@ -73,95 +74,130 @@ static Plan *create_plan_recurse(PlannerInfo *root, Path *best_path, - int flags); + int flags, double est_calls); static Plan *create_scan_plan(PlannerInfo *root, Path *best_path, - int flags); + int flags, double est_calls); +static void plan_consider_jit(PlannerInfo *root, Plan *plan); static List *build_path_tlist(PlannerInfo *root, Path *path); static bool use_physical_tlist(PlannerInfo *root, Path *path, int flags); static List *get_gating_quals(PlannerInfo *root, List *quals); static Plan *create_gating_plan(PlannerInfo *root, Path *path, Plan *plan, - List *gating_quals); -static Plan *create_join_plan(PlannerInfo *root, JoinPath *best_path); + List *gating_quals, double est_calls); +static Plan *create_join_plan(PlannerInfo *root, JoinPath *best_path, + double est_calls); static bool mark_async_capable_plan(Plan *plan, Path *path); static Plan *create_append_plan(PlannerInfo *root, AppendPath *best_path, - int flags); + int flags, double est_calls); static Plan *create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path, - int flags); + int flags, double est_calls); static Result *create_group_result_plan(PlannerInfo *root, - GroupResultPath *best_path); -static ProjectSet *create_project_set_plan(PlannerInfo *root, ProjectSetPath *best_path); + GroupResultPath *best_path, + double est_calls); +static ProjectSet *create_project_set_plan(PlannerInfo *root, ProjectSetPath *best_path, + double est_calls); static Material *create_material_plan(PlannerInfo *root, MaterialPath *best_path, - int flags); + int flags, double est_calls); static Memoize *create_memoize_plan(PlannerInfo *root, MemoizePath *best_path, - int flags); + int flags, double est_calls); static Plan *create_unique_plan(PlannerInfo *root, UniquePath *best_path, - int flags); -static Gather *create_gather_plan(PlannerInfo *root, GatherPath *best_path); + int flags, double est_calls); +static Gather *create_gather_plan(PlannerInfo *root, GatherPath *best_path, + double est_calls); static Plan *create_projection_plan(PlannerInfo *root, ProjectionPath *best_path, - int flags); -static Plan *inject_projection_plan(Plan *subplan, List *tlist, bool parallel_safe); -static Sort *create_sort_plan(PlannerInfo *root, SortPath *best_path, int flags); + int flags, double est_calls); +static Plan *inject_projection_plan(Plan *subplan, List *tlist, bool parallel_safe, + double est_calls); +static Sort *create_sort_plan(PlannerInfo *root, SortPath *best_path, int flags, + double est_calls); static IncrementalSort *create_incrementalsort_plan(PlannerInfo *root, - IncrementalSortPath *best_path, int flags); -static Group *create_group_plan(PlannerInfo *root, GroupPath *best_path); + IncrementalSortPath *best_path, int flags, + double est_calls); +static Group *create_group_plan(PlannerInfo *root, GroupPath *best_path, + double est_calls); static Unique *create_upper_unique_plan(PlannerInfo *root, UpperUniquePath *best_path, - int flags); -static Agg *create_agg_plan(PlannerInfo *root, AggPath *best_path); -static Plan *create_groupingsets_plan(PlannerInfo *root, GroupingSetsPath *best_path); -static Result *create_minmaxagg_plan(PlannerInfo *root, MinMaxAggPath *best_path); -static WindowAgg *create_windowagg_plan(PlannerInfo *root, WindowAggPath *best_path); + int flags, double est_calls); +static Agg *create_agg_plan(PlannerInfo *root, AggPath *best_path, + double est_calls); +static Plan *create_groupingsets_plan(PlannerInfo *root, GroupingSetsPath *best_path, + double est_calls); +static Result *create_minmaxagg_plan(PlannerInfo *root, MinMaxAggPath *best_path, + double est_calls); +static WindowAgg *create_windowagg_plan(PlannerInfo *root, WindowAggPath *best_path, + double est_calls); static SetOp *create_setop_plan(PlannerInfo *root, SetOpPath *best_path, - int flags); -static RecursiveUnion *create_recursiveunion_plan(PlannerInfo *root, RecursiveUnionPath *best_path); + int flags, double est_calls); +static RecursiveUnion *create_recursiveunion_plan(PlannerInfo *root, RecursiveUnionPath *best_path, + double est_calls); static LockRows *create_lockrows_plan(PlannerInfo *root, LockRowsPath *best_path, - int flags); -static ModifyTable *create_modifytable_plan(PlannerInfo *root, ModifyTablePath *best_path); + int flags, double est_calls); +static ModifyTable *create_modifytable_plan(PlannerInfo *root, ModifyTablePath *best_path, + double est_calls); static Limit *create_limit_plan(PlannerInfo *root, LimitPath *best_path, - int flags); + int flags, double est_calls); static SeqScan *create_seqscan_plan(PlannerInfo *root, Path *best_path, - List *tlist, List *scan_clauses); + List *tlist, List *scan_clauses, + double est_calls); static SampleScan *create_samplescan_plan(PlannerInfo *root, Path *best_path, - List *tlist, List *scan_clauses); + List *tlist, List *scan_clauses, + double est_calls); static Scan *create_indexscan_plan(PlannerInfo *root, IndexPath *best_path, - List *tlist, List *scan_clauses, bool indexonly); + List *tlist, List *scan_clauses, bool indexonly, + double est_calls); static BitmapHeapScan *create_bitmap_scan_plan(PlannerInfo *root, BitmapHeapPath *best_path, - List *tlist, List *scan_clauses); + List *tlist, List *scan_clauses, + double est_calls); static Plan *create_bitmap_subplan(PlannerInfo *root, Path *bitmapqual, - List **qual, List **indexqual, List **indexECs); + List **qual, List **indexqual, List **indexECs, + double est_calls); static void bitmap_subplan_mark_shared(Plan *plan); static TidScan *create_tidscan_plan(PlannerInfo *root, TidPath *best_path, - List *tlist, List *scan_clauses); + List *tlist, List *scan_clauses, + double est_calls); static TidRangeScan *create_tidrangescan_plan(PlannerInfo *root, TidRangePath *best_path, List *tlist, - List *scan_clauses); + List *scan_clauses, + double est_calls); static SubqueryScan *create_subqueryscan_plan(PlannerInfo *root, SubqueryScanPath *best_path, - List *tlist, List *scan_clauses); + List *tlist, List *scan_clauses, + double est_calls); static FunctionScan *create_functionscan_plan(PlannerInfo *root, Path *best_path, - List *tlist, List *scan_clauses); + List *tlist, List *scan_clauses, + double est_calls); static ValuesScan *create_valuesscan_plan(PlannerInfo *root, Path *best_path, - List *tlist, List *scan_clauses); + List *tlist, List *scan_clauses, + double est_calls); static TableFuncScan *create_tablefuncscan_plan(PlannerInfo *root, Path *best_path, - List *tlist, List *scan_clauses); + List *tlist, List *scan_clauses, + double est_calls); static CteScan *create_ctescan_plan(PlannerInfo *root, Path *best_path, - List *tlist, List *scan_clauses); + List *tlist, List *scan_clauses, + double est_calls); static NamedTuplestoreScan *create_namedtuplestorescan_plan(PlannerInfo *root, - Path *best_path, List *tlist, List *scan_clauses); + Path *best_path, List *tlist, List *scan_clauses, + double est_calls); static Result *create_resultscan_plan(PlannerInfo *root, Path *best_path, - List *tlist, List *scan_clauses); + List *tlist, List *scan_clauses, + double est_calls); static WorkTableScan *create_worktablescan_plan(PlannerInfo *root, Path *best_path, - List *tlist, List *scan_clauses); + List *tlist, List *scan_clauses, + double est_calls); static ForeignScan *create_foreignscan_plan(PlannerInfo *root, ForeignPath *best_path, - List *tlist, List *scan_clauses); + List *tlist, List *scan_clauses, + double est_calls); static CustomScan *create_customscan_plan(PlannerInfo *root, CustomPath *best_path, - List *tlist, List *scan_clauses); -static NestLoop *create_nestloop_plan(PlannerInfo *root, NestPath *best_path); -static MergeJoin *create_mergejoin_plan(PlannerInfo *root, MergePath *best_path); -static HashJoin *create_hashjoin_plan(PlannerInfo *root, HashPath *best_path); + List *tlist, List *scan_clauses, + double est_calls); +static NestLoop *create_nestloop_plan(PlannerInfo *root, NestPath *best_path, + double est_calls); +static MergeJoin *create_mergejoin_plan(PlannerInfo *root, MergePath *best_path, + double est_calls); +static HashJoin *create_hashjoin_plan(PlannerInfo *root, HashPath *best_path, + double est_calls); static Node *replace_nestloop_params(PlannerInfo *root, Node *expr); static Node *replace_nestloop_params_mutator(Node *node, PlannerInfo *root); static void fix_indexqual_references(PlannerInfo *root, IndexPath *index_path, @@ -269,11 +305,13 @@ static Plan *prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys, AttrNumber **p_sortColIdx, Oid **p_sortOperators, Oid **p_collations, - bool **p_nullsFirst); + bool **p_nullsFirst, + double est_calls); static Sort *make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, - Relids relids); + Relids relids, double est_calls); static IncrementalSort *make_incrementalsort_from_pathkeys(Plan *lefttree, - List *pathkeys, Relids relids, int nPresortedCols); + List *pathkeys, Relids relids, int nPresortedCols, + double est_calls); static Sort *make_sort_from_groupcols(List *groupcls, AttrNumber *grpColIdx, Plan *lefttree); @@ -314,7 +352,8 @@ static ModifyTable *make_modifytable(PlannerInfo *root, Plan *subplan, List *rowMarks, OnConflictExpr *onconflict, List *mergeActionList, int epqParam); static GatherMerge *create_gather_merge_plan(PlannerInfo *root, - GatherMergePath *best_path); + GatherMergePath *best_path, + double est_calls); /* @@ -330,10 +369,12 @@ static GatherMerge *create_gather_merge_plan(PlannerInfo *root, * * best_path is the best access path * + * est_calls is the number of expected times that we'll (re)scan this plan + * * Returns a Plan tree. */ Plan * -create_plan(PlannerInfo *root, Path *best_path) +create_plan(PlannerInfo *root, Path *best_path, double est_calls) { Plan *plan; @@ -345,7 +386,7 @@ create_plan(PlannerInfo *root, Path *best_path) root->curOuterParams = NIL; /* Recursively process the path tree, demanding the correct tlist result */ - plan = create_plan_recurse(root, best_path, CP_EXACT_TLIST); + plan = create_plan_recurse(root, best_path, CP_EXACT_TLIST, est_calls); /* * Make sure the topmost plan node's targetlist exposes the original @@ -384,7 +425,7 @@ create_plan(PlannerInfo *root, Path *best_path) * Recursive guts of create_plan(). */ static Plan * -create_plan_recurse(PlannerInfo *root, Path *best_path, int flags) +create_plan_recurse(PlannerInfo *root, Path *best_path, int flags, double est_calls) { Plan *plan; @@ -409,136 +450,147 @@ create_plan_recurse(PlannerInfo *root, Path *best_path, int flags) case T_NamedTuplestoreScan: case T_ForeignScan: case T_CustomScan: - plan = create_scan_plan(root, best_path, flags); + plan = create_scan_plan(root, best_path, flags, est_calls); break; case T_HashJoin: case T_MergeJoin: case T_NestLoop: plan = create_join_plan(root, - (JoinPath *) best_path); + (JoinPath *) best_path, est_calls); break; case T_Append: plan = create_append_plan(root, (AppendPath *) best_path, - flags); + flags, est_calls); break; case T_MergeAppend: plan = create_merge_append_plan(root, (MergeAppendPath *) best_path, - flags); + flags, est_calls); break; case T_Result: if (IsA(best_path, ProjectionPath)) { plan = create_projection_plan(root, (ProjectionPath *) best_path, - flags); + flags, est_calls); } else if (IsA(best_path, MinMaxAggPath)) { plan = (Plan *) create_minmaxagg_plan(root, - (MinMaxAggPath *) best_path); + (MinMaxAggPath *) best_path, + est_calls); } else if (IsA(best_path, GroupResultPath)) { plan = (Plan *) create_group_result_plan(root, - (GroupResultPath *) best_path); + (GroupResultPath *) best_path, + est_calls); } else { /* Simple RTE_RESULT base relation */ Assert(IsA(best_path, Path)); - plan = create_scan_plan(root, best_path, flags); + plan = create_scan_plan(root, best_path, flags, est_calls); } break; case T_ProjectSet: plan = (Plan *) create_project_set_plan(root, - (ProjectSetPath *) best_path); + (ProjectSetPath *) best_path, + est_calls); break; case T_Material: plan = (Plan *) create_material_plan(root, (MaterialPath *) best_path, - flags); + flags, est_calls); break; case T_Memoize: plan = (Plan *) create_memoize_plan(root, (MemoizePath *) best_path, - flags); + flags, est_calls); break; case T_Unique: if (IsA(best_path, UpperUniquePath)) { plan = (Plan *) create_upper_unique_plan(root, (UpperUniquePath *) best_path, - flags); + flags, est_calls); } else { Assert(IsA(best_path, UniquePath)); plan = create_unique_plan(root, (UniquePath *) best_path, - flags); + flags, est_calls); } break; case T_Gather: plan = (Plan *) create_gather_plan(root, - (GatherPath *) best_path); + (GatherPath *) best_path, + est_calls); break; case T_Sort: plan = (Plan *) create_sort_plan(root, (SortPath *) best_path, - flags); + flags, est_calls); break; case T_IncrementalSort: plan = (Plan *) create_incrementalsort_plan(root, (IncrementalSortPath *) best_path, - flags); + flags, est_calls); break; case T_Group: plan = (Plan *) create_group_plan(root, - (GroupPath *) best_path); + (GroupPath *) best_path, + est_calls); break; case T_Agg: if (IsA(best_path, GroupingSetsPath)) plan = create_groupingsets_plan(root, - (GroupingSetsPath *) best_path); + (GroupingSetsPath *) best_path, + est_calls); else { Assert(IsA(best_path, AggPath)); plan = (Plan *) create_agg_plan(root, - (AggPath *) best_path); + (AggPath *) best_path, + est_calls); } break; case T_WindowAgg: plan = (Plan *) create_windowagg_plan(root, - (WindowAggPath *) best_path); + (WindowAggPath *) best_path, + est_calls); break; case T_SetOp: plan = (Plan *) create_setop_plan(root, (SetOpPath *) best_path, - flags); + flags, est_calls); break; case T_RecursiveUnion: plan = (Plan *) create_recursiveunion_plan(root, - (RecursiveUnionPath *) best_path); + (RecursiveUnionPath *) best_path, + est_calls); break; case T_LockRows: plan = (Plan *) create_lockrows_plan(root, (LockRowsPath *) best_path, - flags); + flags, est_calls); break; case T_ModifyTable: plan = (Plan *) create_modifytable_plan(root, - (ModifyTablePath *) best_path); + (ModifyTablePath *) best_path, + est_calls); break; case T_Limit: plan = (Plan *) create_limit_plan(root, (LimitPath *) best_path, - flags); + flags, est_calls); break; case T_GatherMerge: plan = (Plan *) create_gather_merge_plan(root, - (GatherMergePath *) best_path); + (GatherMergePath *) best_path, + est_calls); break; default: elog(ERROR, "unrecognized node type: %d", @@ -547,15 +599,75 @@ create_plan_recurse(PlannerInfo *root, Path *best_path, int flags) break; } + /* See about switching on JIT for this node */ + plan_consider_jit(root, plan); + return plan; } +static void +plan_consider_jit(PlannerInfo *root, Plan *plan) +{ + int jitflags = root->glob->jitFlags; + + plan->jit = false; + + /* + * For values scans, expressions are only used once, so ensure we don't + * enable JIT for them. + */ + if (IsA(plan, ValuesScan)) + return; + + /* Determine which JIT options to enable for this plan node */ + if (jit_enabled && jit_above_cost >= 0) + { + Cost total_cost; + + /* + * Take into account the number of times that we expect to rescan a + * given plan node. For example, subplans being invoked under the + * inside of a Nested Loop may be rescanned many times. JITing these + * may be more worthwhile. + */ + total_cost = plan->total_cost * plan->est_calls; + + if (total_cost > jit_above_cost) + { + plan->jit = true; + jitflags |= PGJIT_PERFORM; + + /* + * Decide how much effort should be put into generating better code. + */ + if (jit_optimize_above_cost >= 0 && + total_cost > jit_optimize_above_cost) + jitflags |= PGJIT_OPT3; + if (jit_inline_above_cost >= 0 && + total_cost > jit_inline_above_cost) + jitflags |= PGJIT_INLINE; + + /* + * Decide which operations should be JITed. + */ + if (jit_expressions) + jitflags |= PGJIT_EXPR; + if (jit_tuple_deforming) + jitflags |= PGJIT_DEFORM; + + /* Record the maximum flags used by any plan node */ + root->glob->jitFlags |= jitflags; + } + } +} + /* * create_scan_plan * Create a scan plan for the parent relation of 'best_path'. */ static Plan * -create_scan_plan(PlannerInfo *root, Path *best_path, int flags) +create_scan_plan(PlannerInfo *root, Path *best_path, int flags, + double est_calls) { RelOptInfo *rel = best_path->parent; List *scan_clauses; @@ -660,14 +772,16 @@ create_scan_plan(PlannerInfo *root, Path *best_path, int flags) plan = (Plan *) create_seqscan_plan(root, best_path, tlist, - scan_clauses); + scan_clauses, + est_calls); break; case T_SampleScan: plan = (Plan *) create_samplescan_plan(root, best_path, tlist, - scan_clauses); + scan_clauses, + est_calls); break; case T_IndexScan: @@ -675,7 +789,8 @@ create_scan_plan(PlannerInfo *root, Path *best_path, int flags) (IndexPath *) best_path, tlist, scan_clauses, - false); + false, + est_calls); break; case T_IndexOnlyScan: @@ -683,98 +798,112 @@ create_scan_plan(PlannerInfo *root, Path *best_path, int flags) (IndexPath *) best_path, tlist, scan_clauses, - true); + true, + est_calls); break; case T_BitmapHeapScan: plan = (Plan *) create_bitmap_scan_plan(root, (BitmapHeapPath *) best_path, tlist, - scan_clauses); + scan_clauses, + est_calls); break; case T_TidScan: plan = (Plan *) create_tidscan_plan(root, (TidPath *) best_path, tlist, - scan_clauses); + scan_clauses, + est_calls); break; case T_TidRangeScan: plan = (Plan *) create_tidrangescan_plan(root, (TidRangePath *) best_path, tlist, - scan_clauses); + scan_clauses, + est_calls); break; case T_SubqueryScan: plan = (Plan *) create_subqueryscan_plan(root, (SubqueryScanPath *) best_path, tlist, - scan_clauses); + scan_clauses, + est_calls); break; case T_FunctionScan: plan = (Plan *) create_functionscan_plan(root, best_path, tlist, - scan_clauses); + scan_clauses, + est_calls); break; case T_TableFuncScan: plan = (Plan *) create_tablefuncscan_plan(root, best_path, tlist, - scan_clauses); + scan_clauses, + est_calls); break; case T_ValuesScan: plan = (Plan *) create_valuesscan_plan(root, best_path, tlist, - scan_clauses); + scan_clauses, + est_calls); break; case T_CteScan: plan = (Plan *) create_ctescan_plan(root, best_path, tlist, - scan_clauses); + scan_clauses, + est_calls); break; case T_NamedTuplestoreScan: plan = (Plan *) create_namedtuplestorescan_plan(root, best_path, tlist, - scan_clauses); + scan_clauses, + est_calls); break; case T_Result: plan = (Plan *) create_resultscan_plan(root, best_path, tlist, - scan_clauses); + scan_clauses, + est_calls); break; case T_WorkTableScan: plan = (Plan *) create_worktablescan_plan(root, best_path, tlist, - scan_clauses); + scan_clauses, + est_calls); break; case T_ForeignScan: plan = (Plan *) create_foreignscan_plan(root, (ForeignPath *) best_path, tlist, - scan_clauses); + scan_clauses, + est_calls); break; case T_CustomScan: plan = (Plan *) create_customscan_plan(root, (CustomPath *) best_path, tlist, - scan_clauses); + scan_clauses, + est_calls); break; default: @@ -790,7 +919,8 @@ create_scan_plan(PlannerInfo *root, Path *best_path, int flags) * quals. */ if (gating_clauses) - plan = create_gating_plan(root, best_path, plan, gating_clauses); + plan = create_gating_plan(root, best_path, plan, gating_clauses, + est_calls); return plan; } @@ -1000,7 +1130,7 @@ get_gating_quals(PlannerInfo *root, List *quals) */ static Plan * create_gating_plan(PlannerInfo *root, Path *path, Plan *plan, - List *gating_quals) + List *gating_quals, double est_calls) { Plan *gplan; Plan *splan; @@ -1045,6 +1175,7 @@ create_gating_plan(PlannerInfo *root, Path *path, Plan *plan, * gating qual being true. */ copy_plan_costsize(gplan, plan); + gplan->est_calls = clamp_row_est(est_calls); /* Gating quals could be unsafe, so better use the Path's safety flag */ gplan->parallel_safe = path->parallel_safe; @@ -1058,7 +1189,7 @@ create_gating_plan(PlannerInfo *root, Path *path, Plan *plan, * inner and outer paths. */ static Plan * -create_join_plan(PlannerInfo *root, JoinPath *best_path) +create_join_plan(PlannerInfo *root, JoinPath *best_path, double est_calls) { Plan *plan; List *gating_clauses; @@ -1067,15 +1198,18 @@ create_join_plan(PlannerInfo *root, JoinPath *best_path) { case T_MergeJoin: plan = (Plan *) create_mergejoin_plan(root, - (MergePath *) best_path); + (MergePath *) best_path, + est_calls); break; case T_HashJoin: plan = (Plan *) create_hashjoin_plan(root, - (HashPath *) best_path); + (HashPath *) best_path, + est_calls); break; case T_NestLoop: plan = (Plan *) create_nestloop_plan(root, - (NestPath *) best_path); + (NestPath *) best_path, + est_calls); break; default: elog(ERROR, "unrecognized node type: %d", @@ -1092,7 +1226,7 @@ create_join_plan(PlannerInfo *root, JoinPath *best_path) gating_clauses = get_gating_quals(root, best_path->joinrestrictinfo); if (gating_clauses) plan = create_gating_plan(root, (Path *) best_path, plan, - gating_clauses); + gating_clauses, est_calls); #ifdef NOT_USED @@ -1107,6 +1241,8 @@ create_join_plan(PlannerInfo *root, JoinPath *best_path) get_actual_clauses(get_loc_restrictinfo(best_path)))); #endif + plan->est_calls = clamp_row_est(est_calls); + return plan; } @@ -1173,7 +1309,8 @@ mark_async_capable_plan(Plan *plan, Path *path) * Returns a Plan node. */ static Plan * -create_append_plan(PlannerInfo *root, AppendPath *best_path, int flags) +create_append_plan(PlannerInfo *root, AppendPath *best_path, int flags, + double est_calls) { Append *plan; List *tlist = build_path_tlist(root, &best_path->path); @@ -1250,7 +1387,8 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path, int flags) &nodeSortColIdx, &nodeSortOperators, &nodeCollations, - &nodeNullsFirst); + &nodeNullsFirst, + est_calls); tlist_was_changed = (orig_tlist_length != list_length(plan->plan.targetlist)); } @@ -1266,7 +1404,7 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path, int flags) Plan *subplan; /* Must insist that all children return the same tlist */ - subplan = create_plan_recurse(root, subpath, CP_EXACT_TLIST); + subplan = create_plan_recurse(root, subpath, CP_EXACT_TLIST, est_calls); /* * For ordered Appends, we must insert a Sort node if subplan isn't @@ -1294,7 +1432,8 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path, int flags) &sortColIdx, &sortOperators, &collations, - &nullsFirst); + &nullsFirst, + est_calls); /* * Check that we got the same sort key information. We just @@ -1370,6 +1509,7 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path, int flags) plan->part_prune_info = partpruneinfo; copy_generic_path_info(&plan->plan, (Path *) best_path); + plan->plan.est_calls = clamp_row_est(est_calls); /* * If prepare_sort_from_pathkeys added sort columns, but we were told to @@ -1381,7 +1521,7 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path, int flags) tlist = list_truncate(list_copy(plan->plan.targetlist), orig_tlist_length); return inject_projection_plan((Plan *) plan, tlist, - plan->plan.parallel_safe); + plan->plan.parallel_safe, est_calls); } else return (Plan *) plan; @@ -1396,7 +1536,7 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path, int flags) */ static Plan * create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path, - int flags) + int flags, double est_calls) { MergeAppend *node = makeNode(MergeAppend); Plan *plan = &node->plan; @@ -1416,6 +1556,7 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path, * child plans, to make cross-checking the sort info easier. */ copy_generic_path_info(plan, (Path *) best_path); + plan->est_calls = clamp_row_est(est_calls); plan->targetlist = tlist; plan->qual = NIL; plan->lefttree = NULL; @@ -1436,7 +1577,8 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path, &node->sortColIdx, &node->sortOperators, &node->collations, - &node->nullsFirst); + &node->nullsFirst, + est_calls); tlist_was_changed = (orig_tlist_length != list_length(plan->targetlist)); /* @@ -1456,7 +1598,7 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path, /* Build the child plan */ /* Must insist that all children return the same tlist */ - subplan = create_plan_recurse(root, subpath, CP_EXACT_TLIST); + subplan = create_plan_recurse(root, subpath, CP_EXACT_TLIST, est_calls); /* Compute sort column info, and adjust subplan's tlist as needed */ subplan = prepare_sort_from_pathkeys(subplan, pathkeys, @@ -1467,7 +1609,8 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path, &sortColIdx, &sortOperators, &collations, - &nullsFirst); + &nullsFirst, + est_calls); /* * Check that we got the same sort key information. We just Assert @@ -1539,7 +1682,8 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path, if (tlist_was_changed && (flags & (CP_EXACT_TLIST | CP_SMALL_TLIST))) { tlist = list_truncate(list_copy(plan->targetlist), orig_tlist_length); - return inject_projection_plan(plan, tlist, plan->parallel_safe); + return inject_projection_plan(plan, tlist, plan->parallel_safe, + est_calls); } else return plan; @@ -1553,7 +1697,8 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path, * Returns a Plan node. */ static Result * -create_group_result_plan(PlannerInfo *root, GroupResultPath *best_path) +create_group_result_plan(PlannerInfo *root, GroupResultPath *best_path, + double est_calls) { Result *plan; List *tlist; @@ -1567,6 +1712,7 @@ create_group_result_plan(PlannerInfo *root, GroupResultPath *best_path) plan = make_result(tlist, (Node *) quals, NULL); copy_generic_path_info(&plan->plan, (Path *) best_path); + plan->plan.est_calls = clamp_row_est(est_calls); return plan; } @@ -1578,20 +1724,22 @@ create_group_result_plan(PlannerInfo *root, GroupResultPath *best_path) * Returns a Plan node. */ static ProjectSet * -create_project_set_plan(PlannerInfo *root, ProjectSetPath *best_path) +create_project_set_plan(PlannerInfo *root, ProjectSetPath *best_path, + double est_calls) { ProjectSet *plan; Plan *subplan; List *tlist; /* Since we intend to project, we don't need to constrain child tlist */ - subplan = create_plan_recurse(root, best_path->subpath, 0); + subplan = create_plan_recurse(root, best_path->subpath, 0, est_calls); tlist = build_path_tlist(root, &best_path->path); plan = make_project_set(tlist, subplan); copy_generic_path_info(&plan->plan, (Path *) best_path); + plan->plan.est_calls = clamp_row_est(est_calls); return plan; } @@ -1604,7 +1752,8 @@ create_project_set_plan(PlannerInfo *root, ProjectSetPath *best_path) * Returns a Plan node. */ static Material * -create_material_plan(PlannerInfo *root, MaterialPath *best_path, int flags) +create_material_plan(PlannerInfo *root, MaterialPath *best_path, int flags, + double est_calls) { Material *plan; Plan *subplan; @@ -1612,14 +1761,21 @@ create_material_plan(PlannerInfo *root, MaterialPath *best_path, int flags) /* * We don't want any excess columns in the materialized tuples, so request * a smaller tlist. Otherwise, since Material doesn't project, tlist - * requirements pass through. + * requirements pass through. Here we also don't propagate the est_calls + * to the subplan. We assume that the Material node will only call its + * subplan once and then returned the cached version on each subsequent + * execution. This might not be true when a parameter change causes the + * Material node to have to rescan, but that's hard to estimate here and + * the current usages of est_calls does not seem important enough to + * warrant expending too much effort trying to calculate this. */ subplan = create_plan_recurse(root, best_path->subpath, - flags | CP_SMALL_TLIST); + flags | CP_SMALL_TLIST, 1.0); plan = make_material(subplan); copy_generic_path_info(&plan->plan, (Path *) best_path); + plan->plan.est_calls = clamp_row_est(est_calls); return plan; } @@ -1632,7 +1788,8 @@ create_material_plan(PlannerInfo *root, MaterialPath *best_path, int flags) * Returns a Plan node. */ static Memoize * -create_memoize_plan(PlannerInfo *root, MemoizePath *best_path, int flags) +create_memoize_plan(PlannerInfo *root, MemoizePath *best_path, int flags, + double est_calls) { Memoize *plan; Bitmapset *keyparamids; @@ -1645,8 +1802,13 @@ create_memoize_plan(PlannerInfo *root, MemoizePath *best_path, int flags) int nkeys; int i; + /* + * est_calls must take into account the expected hit ratio of the cache. + * We'll only be calling the subplan when it's a cache miss. + */ subplan = create_plan_recurse(root, best_path->subpath, - flags | CP_SMALL_TLIST); + flags | CP_SMALL_TLIST, + est_calls * (1.0 - best_path->est_hitratio)); param_exprs = (List *) replace_nestloop_params(root, (Node *) best_path->param_exprs); @@ -1674,6 +1836,7 @@ create_memoize_plan(PlannerInfo *root, MemoizePath *best_path, int flags) best_path->est_entries, keyparamids); copy_generic_path_info(&plan->plan, (Path *) best_path); + plan->plan.est_calls = clamp_row_est(est_calls); return plan; } @@ -1686,7 +1849,8 @@ create_memoize_plan(PlannerInfo *root, MemoizePath *best_path, int flags) * Returns a Plan node. */ static Plan * -create_unique_plan(PlannerInfo *root, UniquePath *best_path, int flags) +create_unique_plan(PlannerInfo *root, UniquePath *best_path, int flags, + double est_calls) { Plan *plan; Plan *subplan; @@ -1702,7 +1866,7 @@ create_unique_plan(PlannerInfo *root, UniquePath *best_path, int flags) ListCell *l; /* Unique doesn't project, so tlist requirements pass through */ - subplan = create_plan_recurse(root, best_path->subpath, flags); + subplan = create_plan_recurse(root, best_path->subpath, flags, est_calls); /* Done if we don't need to do any actual unique-ifying */ if (best_path->umethod == UNIQUE_PATH_NOOP) @@ -1753,7 +1917,8 @@ create_unique_plan(PlannerInfo *root, UniquePath *best_path, int flags) /* Use change_plan_targetlist in case we need to insert a Result node */ if (newitems || best_path->umethod == UNIQUE_PATH_SORT) subplan = change_plan_targetlist(subplan, newtlist, - best_path->path.parallel_safe); + best_path->path.parallel_safe, + est_calls); /* * Build control information showing which subplan output columns are to @@ -1874,6 +2039,7 @@ create_unique_plan(PlannerInfo *root, UniquePath *best_path, int flags) /* Copy cost data from Path to Plan */ copy_generic_path_info(plan, &best_path->path); + plan->est_calls = clamp_row_est(est_calls); return plan; } @@ -1885,7 +2051,7 @@ create_unique_plan(PlannerInfo *root, UniquePath *best_path, int flags) * for its subpaths. */ static Gather * -create_gather_plan(PlannerInfo *root, GatherPath *best_path) +create_gather_plan(PlannerInfo *root, GatherPath *best_path, double est_calls) { Gather *gather_plan; Plan *subplan; @@ -1897,7 +2063,8 @@ create_gather_plan(PlannerInfo *root, GatherPath *best_path) * can't travel through a tuple queue because it uses MinimalTuple * representation). */ - subplan = create_plan_recurse(root, best_path->subpath, CP_EXACT_TLIST); + subplan = create_plan_recurse(root, best_path->subpath, CP_EXACT_TLIST, + est_calls); tlist = build_path_tlist(root, &best_path->path); @@ -1909,6 +2076,7 @@ create_gather_plan(PlannerInfo *root, GatherPath *best_path) subplan); copy_generic_path_info(&gather_plan->plan, &best_path->path); + gather_plan->plan.est_calls = clamp_row_est(est_calls); /* use parallel mode for parallel plans. */ root->glob->parallelModeNeeded = true; @@ -1923,7 +2091,8 @@ create_gather_plan(PlannerInfo *root, GatherPath *best_path) * plans for its subpaths. */ static GatherMerge * -create_gather_merge_plan(PlannerInfo *root, GatherMergePath *best_path) +create_gather_merge_plan(PlannerInfo *root, GatherMergePath *best_path, + double est_calls) { GatherMerge *gm_plan; Plan *subplan; @@ -1931,13 +2100,15 @@ create_gather_merge_plan(PlannerInfo *root, GatherMergePath *best_path) List *tlist = build_path_tlist(root, &best_path->path); /* As with Gather, project away columns in the workers. */ - subplan = create_plan_recurse(root, best_path->subpath, CP_EXACT_TLIST); + subplan = create_plan_recurse(root, best_path->subpath, CP_EXACT_TLIST, + est_calls); /* Create a shell for a GatherMerge plan. */ gm_plan = makeNode(GatherMerge); gm_plan->plan.targetlist = tlist; gm_plan->num_workers = best_path->num_workers; copy_generic_path_info(&gm_plan->plan, &best_path->path); + gm_plan->plan.est_calls = clamp_row_est(est_calls); /* Assign the rescan Param. */ gm_plan->rescan_param = assign_special_exec_param(root); @@ -1954,7 +2125,8 @@ create_gather_merge_plan(PlannerInfo *root, GatherMergePath *best_path) &gm_plan->sortColIdx, &gm_plan->sortOperators, &gm_plan->collations, - &gm_plan->nullsFirst); + &gm_plan->nullsFirst, + est_calls); /* @@ -1984,7 +2156,8 @@ create_gather_merge_plan(PlannerInfo *root, GatherMergePath *best_path) * but sometimes we can just let the subplan do the work. */ static Plan * -create_projection_plan(PlannerInfo *root, ProjectionPath *best_path, int flags) +create_projection_plan(PlannerInfo *root, ProjectionPath *best_path, int flags, + double est_calls) { Plan *plan; Plan *subplan; @@ -2011,7 +2184,7 @@ create_projection_plan(PlannerInfo *root, ProjectionPath *best_path, int flags) * actually need to project. However, we may still need to ensure * proper sortgroupref labels, if the caller cares about those. */ - subplan = create_plan_recurse(root, best_path->subpath, 0); + subplan = create_plan_recurse(root, best_path->subpath, 0, est_calls); tlist = subplan->targetlist; if (flags & CP_LABEL_TLIST) apply_pathtarget_labeling_to_tlist(tlist, @@ -2026,7 +2199,7 @@ create_projection_plan(PlannerInfo *root, ProjectionPath *best_path, int flags) * produces. */ subplan = create_plan_recurse(root, best_path->subpath, - CP_IGNORE_TLIST); + CP_IGNORE_TLIST, est_calls); Assert(is_projection_capable_plan(subplan)); tlist = build_path_tlist(root, &best_path->path); } @@ -2036,7 +2209,7 @@ create_projection_plan(PlannerInfo *root, ProjectionPath *best_path, int flags) * It looks like we need a result node, unless by good fortune the * requested tlist is exactly the one the child wants to produce. */ - subplan = create_plan_recurse(root, best_path->subpath, 0); + subplan = create_plan_recurse(root, best_path->subpath, 0, est_calls); tlist = build_path_tlist(root, &best_path->path); needs_result_node = !tlist_same_exprs(tlist, subplan->targetlist); } @@ -2069,6 +2242,7 @@ create_projection_plan(PlannerInfo *root, ProjectionPath *best_path, int flags) plan = (Plan *) make_result(tlist, NULL, subplan); copy_generic_path_info(plan, (Path *) best_path); + plan->est_calls = clamp_row_est(est_calls); } return plan; @@ -2086,7 +2260,8 @@ create_projection_plan(PlannerInfo *root, ProjectionPath *best_path, int flags) * to apply (since the tlist might be unsafe even if the child plan is safe). */ static Plan * -inject_projection_plan(Plan *subplan, List *tlist, bool parallel_safe) +inject_projection_plan(Plan *subplan, List *tlist, bool parallel_safe, + double est_calls) { Plan *plan; @@ -2100,6 +2275,7 @@ inject_projection_plan(Plan *subplan, List *tlist, bool parallel_safe) * consistent not more so. Hence, just copy the subplan's cost. */ copy_plan_costsize(plan, subplan); + plan->est_calls = clamp_row_est(est_calls); plan->parallel_safe = parallel_safe; return plan; @@ -2118,7 +2294,8 @@ inject_projection_plan(Plan *subplan, List *tlist, bool parallel_safe) * flag of the FDW's own Path node. */ Plan * -change_plan_targetlist(Plan *subplan, List *tlist, bool tlist_parallel_safe) +change_plan_targetlist(Plan *subplan, List *tlist, bool tlist_parallel_safe, + double est_calls) { /* * If the top plan node can't do projections and its existing target list @@ -2129,7 +2306,7 @@ change_plan_targetlist(Plan *subplan, List *tlist, bool tlist_parallel_safe) !tlist_same_exprs(tlist, subplan->targetlist)) subplan = inject_projection_plan(subplan, tlist, subplan->parallel_safe && - tlist_parallel_safe); + tlist_parallel_safe, est_calls); else { /* Else we can just replace the plan node's tlist */ @@ -2146,7 +2323,8 @@ change_plan_targetlist(Plan *subplan, List *tlist, bool tlist_parallel_safe) * for its subpaths. */ static Sort * -create_sort_plan(PlannerInfo *root, SortPath *best_path, int flags) +create_sort_plan(PlannerInfo *root, SortPath *best_path, int flags, + double est_calls) { Sort *plan; Plan *subplan; @@ -2157,7 +2335,7 @@ create_sort_plan(PlannerInfo *root, SortPath *best_path, int flags) * requirements pass through. */ subplan = create_plan_recurse(root, best_path->subpath, - flags | CP_SMALL_TLIST); + flags | CP_SMALL_TLIST, est_calls); /* * make_sort_from_pathkeys indirectly calls find_ec_member_matching_expr, @@ -2167,9 +2345,11 @@ create_sort_plan(PlannerInfo *root, SortPath *best_path, int flags) */ plan = make_sort_from_pathkeys(subplan, best_path->path.pathkeys, IS_OTHER_REL(best_path->subpath->parent) ? - best_path->path.parent->relids : NULL); + best_path->path.parent->relids : NULL, + est_calls); copy_generic_path_info(&plan->plan, (Path *) best_path); + plan->plan.est_calls = clamp_row_est(est_calls); return plan; } @@ -2181,21 +2361,22 @@ create_sort_plan(PlannerInfo *root, SortPath *best_path, int flags) */ static IncrementalSort * create_incrementalsort_plan(PlannerInfo *root, IncrementalSortPath *best_path, - int flags) + int flags, double est_calls) { IncrementalSort *plan; Plan *subplan; /* See comments in create_sort_plan() above */ subplan = create_plan_recurse(root, best_path->spath.subpath, - flags | CP_SMALL_TLIST); + flags | CP_SMALL_TLIST, est_calls); plan = make_incrementalsort_from_pathkeys(subplan, best_path->spath.path.pathkeys, IS_OTHER_REL(best_path->spath.subpath->parent) ? best_path->spath.path.parent->relids : NULL, - best_path->nPresortedCols); + best_path->nPresortedCols, est_calls); copy_generic_path_info(&plan->sort.plan, (Path *) best_path); + plan->sort.plan.est_calls = clamp_row_est(est_calls); return plan; } @@ -2207,7 +2388,7 @@ create_incrementalsort_plan(PlannerInfo *root, IncrementalSortPath *best_path, * for its subpaths. */ static Group * -create_group_plan(PlannerInfo *root, GroupPath *best_path) +create_group_plan(PlannerInfo *root, GroupPath *best_path, double est_calls) { Group *plan; Plan *subplan; @@ -2218,7 +2399,8 @@ create_group_plan(PlannerInfo *root, GroupPath *best_path) * Group can project, so no need to be terribly picky about child tlist, * but we do need grouping columns to be available */ - subplan = create_plan_recurse(root, best_path->subpath, CP_LABEL_TLIST); + subplan = create_plan_recurse(root, best_path->subpath, CP_LABEL_TLIST, + est_calls); tlist = build_path_tlist(root, &best_path->path); @@ -2235,6 +2417,7 @@ create_group_plan(PlannerInfo *root, GroupPath *best_path) subplan); copy_generic_path_info(&plan->plan, (Path *) best_path); + plan->plan.est_calls = clamp_row_est(est_calls); return plan; } @@ -2246,7 +2429,8 @@ create_group_plan(PlannerInfo *root, GroupPath *best_path) * for its subpaths. */ static Unique * -create_upper_unique_plan(PlannerInfo *root, UpperUniquePath *best_path, int flags) +create_upper_unique_plan(PlannerInfo *root, UpperUniquePath *best_path, int flags, + double est_calls) { Unique *plan; Plan *subplan; @@ -2256,13 +2440,14 @@ create_upper_unique_plan(PlannerInfo *root, UpperUniquePath *best_path, int flag * need grouping columns to be labeled. */ subplan = create_plan_recurse(root, best_path->subpath, - flags | CP_LABEL_TLIST); + flags | CP_LABEL_TLIST, est_calls); plan = make_unique_from_pathkeys(subplan, best_path->path.pathkeys, best_path->numkeys); copy_generic_path_info(&plan->plan, (Path *) best_path); + plan->plan.est_calls = clamp_row_est(est_calls); return plan; } @@ -2274,7 +2459,7 @@ create_upper_unique_plan(PlannerInfo *root, UpperUniquePath *best_path, int flag * for its subpaths. */ static Agg * -create_agg_plan(PlannerInfo *root, AggPath *best_path) +create_agg_plan(PlannerInfo *root, AggPath *best_path, double est_calls) { Agg *plan; Plan *subplan; @@ -2285,7 +2470,8 @@ create_agg_plan(PlannerInfo *root, AggPath *best_path) * Agg can project, so no need to be terribly picky about child tlist, but * we do need grouping columns to be available */ - subplan = create_plan_recurse(root, best_path->subpath, CP_LABEL_TLIST); + subplan = create_plan_recurse(root, best_path->subpath, CP_LABEL_TLIST, + est_calls); tlist = build_path_tlist(root, &best_path->path); @@ -2307,6 +2493,7 @@ create_agg_plan(PlannerInfo *root, AggPath *best_path) subplan); copy_generic_path_info(&plan->plan, (Path *) best_path); + plan->plan.est_calls = clamp_row_est(est_calls); return plan; } @@ -2358,7 +2545,8 @@ remap_groupColIdx(PlannerInfo *root, List *groupClause) * Returns a Plan node. */ static Plan * -create_groupingsets_plan(PlannerInfo *root, GroupingSetsPath *best_path) +create_groupingsets_plan(PlannerInfo *root, GroupingSetsPath *best_path, + double est_calls) { Agg *plan; Plan *subplan; @@ -2376,7 +2564,8 @@ create_groupingsets_plan(PlannerInfo *root, GroupingSetsPath *best_path) * Agg can project, so no need to be terribly picky about child tlist, but * we do need grouping columns to be available */ - subplan = create_plan_recurse(root, best_path->subpath, CP_LABEL_TLIST); + subplan = create_plan_recurse(root, best_path->subpath, CP_LABEL_TLIST, + est_calls); /* * Compute the mapping from tleSortGroupRef to column index in the child's @@ -2471,7 +2660,7 @@ create_groupingsets_plan(PlannerInfo *root, GroupingSetsPath *best_path) sort_plan->targetlist = NIL; sort_plan->lefttree = NULL; } - + /* XXX do we need to record est_calls here? */ chain = lappend(chain, agg_plan); } } @@ -2504,6 +2693,7 @@ create_groupingsets_plan(PlannerInfo *root, GroupingSetsPath *best_path) /* Copy cost data from Path to Plan */ copy_generic_path_info(&plan->plan, &best_path->path); + plan->plan.est_calls = clamp_row_est(est_calls); } return (Plan *) plan; @@ -2516,7 +2706,8 @@ create_groupingsets_plan(PlannerInfo *root, GroupingSetsPath *best_path) * for its subpaths. */ static Result * -create_minmaxagg_plan(PlannerInfo *root, MinMaxAggPath *best_path) +create_minmaxagg_plan(PlannerInfo *root, MinMaxAggPath *best_path, + double est_calls) { Result *plan; List *tlist; @@ -2536,7 +2727,7 @@ create_minmaxagg_plan(PlannerInfo *root, MinMaxAggPath *best_path) * Since we are entering a different planner context (subroot), * recurse to create_plan not create_plan_recurse. */ - plan = create_plan(subroot, mminfo->path); + plan = create_plan(subroot, mminfo->path, est_calls); plan = (Plan *) make_limit(plan, subparse->limitOffset, @@ -2562,6 +2753,7 @@ create_minmaxagg_plan(PlannerInfo *root, MinMaxAggPath *best_path) plan = make_result(tlist, (Node *) best_path->quals, NULL); copy_generic_path_info(&plan->plan, (Path *) best_path); + plan->plan.est_calls = clamp_row_est(est_calls); /* * During setrefs.c, we'll need to replace references to the Agg nodes @@ -2582,7 +2774,8 @@ create_minmaxagg_plan(PlannerInfo *root, MinMaxAggPath *best_path) * for its subpaths. */ static WindowAgg * -create_windowagg_plan(PlannerInfo *root, WindowAggPath *best_path) +create_windowagg_plan(PlannerInfo *root, WindowAggPath *best_path, + double est_calls) { WindowAgg *plan; WindowClause *wc = best_path->winclause; @@ -2607,7 +2800,7 @@ create_windowagg_plan(PlannerInfo *root, WindowAggPath *best_path) * course need grouping columns to be available. */ subplan = create_plan_recurse(root, best_path->subpath, - CP_LABEL_TLIST | CP_SMALL_TLIST); + CP_LABEL_TLIST | CP_SMALL_TLIST, est_calls); tlist = build_path_tlist(root, &best_path->path); @@ -2679,6 +2872,7 @@ create_windowagg_plan(PlannerInfo *root, WindowAggPath *best_path) subplan); copy_generic_path_info(&plan->plan, (Path *) best_path); + plan->plan.est_calls = clamp_row_est(est_calls); return plan; } @@ -2690,7 +2884,8 @@ create_windowagg_plan(PlannerInfo *root, WindowAggPath *best_path) * for its subpaths. */ static SetOp * -create_setop_plan(PlannerInfo *root, SetOpPath *best_path, int flags) +create_setop_plan(PlannerInfo *root, SetOpPath *best_path, int flags, + double est_calls) { SetOp *plan; Plan *subplan; @@ -2701,7 +2896,7 @@ create_setop_plan(PlannerInfo *root, SetOpPath *best_path, int flags) * need grouping columns to be labeled. */ subplan = create_plan_recurse(root, best_path->subpath, - flags | CP_LABEL_TLIST); + flags | CP_LABEL_TLIST, est_calls); /* Convert numGroups to long int --- but 'ware overflow! */ numGroups = (long) Min(best_path->numGroups, (double) LONG_MAX); @@ -2715,6 +2910,7 @@ create_setop_plan(PlannerInfo *root, SetOpPath *best_path, int flags) numGroups); copy_generic_path_info(&plan->plan, (Path *) best_path); + plan->plan.est_calls = clamp_row_est(est_calls); return plan; } @@ -2726,7 +2922,8 @@ create_setop_plan(PlannerInfo *root, SetOpPath *best_path, int flags) * for its subpaths. */ static RecursiveUnion * -create_recursiveunion_plan(PlannerInfo *root, RecursiveUnionPath *best_path) +create_recursiveunion_plan(PlannerInfo *root, RecursiveUnionPath *best_path, + double est_calls) { RecursiveUnion *plan; Plan *leftplan; @@ -2735,8 +2932,10 @@ create_recursiveunion_plan(PlannerInfo *root, RecursiveUnionPath *best_path) long numGroups; /* Need both children to produce same tlist, so force it */ - leftplan = create_plan_recurse(root, best_path->leftpath, CP_EXACT_TLIST); - rightplan = create_plan_recurse(root, best_path->rightpath, CP_EXACT_TLIST); + leftplan = create_plan_recurse(root, best_path->leftpath, CP_EXACT_TLIST, + est_calls); + rightplan = create_plan_recurse(root, best_path->rightpath, CP_EXACT_TLIST, + est_calls); tlist = build_path_tlist(root, &best_path->path); @@ -2751,6 +2950,7 @@ create_recursiveunion_plan(PlannerInfo *root, RecursiveUnionPath *best_path) numGroups); copy_generic_path_info(&plan->plan, (Path *) best_path); + plan->plan.est_calls = clamp_row_est(est_calls); return plan; } @@ -2763,17 +2963,18 @@ create_recursiveunion_plan(PlannerInfo *root, RecursiveUnionPath *best_path) */ static LockRows * create_lockrows_plan(PlannerInfo *root, LockRowsPath *best_path, - int flags) + int flags, double est_calls) { LockRows *plan; Plan *subplan; /* LockRows doesn't project, so tlist requirements pass through */ - subplan = create_plan_recurse(root, best_path->subpath, flags); + subplan = create_plan_recurse(root, best_path->subpath, flags, est_calls); plan = make_lockrows(subplan, best_path->rowMarks, best_path->epqParam); copy_generic_path_info(&plan->plan, (Path *) best_path); + plan->plan.est_calls = clamp_row_est(est_calls); return plan; } @@ -2785,14 +2986,15 @@ create_lockrows_plan(PlannerInfo *root, LockRowsPath *best_path, * Returns a Plan node. */ static ModifyTable * -create_modifytable_plan(PlannerInfo *root, ModifyTablePath *best_path) +create_modifytable_plan(PlannerInfo *root, ModifyTablePath *best_path, + double est_calls) { ModifyTable *plan; Path *subpath = best_path->subpath; Plan *subplan; /* Subplan must produce exactly the specified tlist */ - subplan = create_plan_recurse(root, subpath, CP_EXACT_TLIST); + subplan = create_plan_recurse(root, subpath, CP_EXACT_TLIST, est_calls); /* Transfer resname/resjunk labeling, too, to keep executor happy */ apply_tlist_labeling(subplan->targetlist, root->processed_tlist); @@ -2814,6 +3016,7 @@ create_modifytable_plan(PlannerInfo *root, ModifyTablePath *best_path) best_path->epqParam); copy_generic_path_info(&plan->plan, &best_path->path); + plan->plan.est_calls = clamp_row_est(est_calls); return plan; } @@ -2825,7 +3028,8 @@ create_modifytable_plan(PlannerInfo *root, ModifyTablePath *best_path) * for its subpaths. */ static Limit * -create_limit_plan(PlannerInfo *root, LimitPath *best_path, int flags) +create_limit_plan(PlannerInfo *root, LimitPath *best_path, int flags, + double est_calls) { Limit *plan; Plan *subplan; @@ -2835,7 +3039,7 @@ create_limit_plan(PlannerInfo *root, LimitPath *best_path, int flags) Oid *uniqCollations = NULL; /* Limit doesn't project, so tlist requirements pass through */ - subplan = create_plan_recurse(root, best_path->subpath, flags); + subplan = create_plan_recurse(root, best_path->subpath, flags, est_calls); /* Extract information necessary for comparing rows for WITH TIES. */ if (best_path->limitOption == LIMIT_OPTION_WITH_TIES) @@ -2868,6 +3072,7 @@ create_limit_plan(PlannerInfo *root, LimitPath *best_path, int flags) numUniqkeys, uniqColIdx, uniqOperators, uniqCollations); copy_generic_path_info(&plan->plan, (Path *) best_path); + plan->plan.est_calls = clamp_row_est(est_calls); return plan; } @@ -2887,7 +3092,7 @@ create_limit_plan(PlannerInfo *root, LimitPath *best_path, int flags) */ static SeqScan * create_seqscan_plan(PlannerInfo *root, Path *best_path, - List *tlist, List *scan_clauses) + List *tlist, List *scan_clauses, double est_calls) { SeqScan *scan_plan; Index scan_relid = best_path->parent->relid; @@ -2914,6 +3119,7 @@ create_seqscan_plan(PlannerInfo *root, Path *best_path, scan_relid); copy_generic_path_info(&scan_plan->scan.plan, best_path); + scan_plan->scan.plan.est_calls = clamp_row_est(est_calls); return scan_plan; } @@ -2925,7 +3131,7 @@ create_seqscan_plan(PlannerInfo *root, Path *best_path, */ static SampleScan * create_samplescan_plan(PlannerInfo *root, Path *best_path, - List *tlist, List *scan_clauses) + List *tlist, List *scan_clauses, double est_calls) { SampleScan *scan_plan; Index scan_relid = best_path->parent->relid; @@ -2960,6 +3166,7 @@ create_samplescan_plan(PlannerInfo *root, Path *best_path, tsc); copy_generic_path_info(&scan_plan->scan.plan, best_path); + scan_plan->scan.plan.est_calls = clamp_row_est(est_calls); return scan_plan; } @@ -2979,7 +3186,7 @@ create_indexscan_plan(PlannerInfo *root, IndexPath *best_path, List *tlist, List *scan_clauses, - bool indexonly) + bool indexonly, double est_calls) { Scan *scan_plan; List *indexclauses = best_path->indexclauses; @@ -3158,6 +3365,7 @@ create_indexscan_plan(PlannerInfo *root, best_path->indexscandir); copy_generic_path_info(&scan_plan->plan, &best_path->path); + scan_plan->plan.est_calls = clamp_row_est(est_calls); return scan_plan; } @@ -3171,7 +3379,7 @@ static BitmapHeapScan * create_bitmap_scan_plan(PlannerInfo *root, BitmapHeapPath *best_path, List *tlist, - List *scan_clauses) + List *scan_clauses, double est_calls) { Index baserelid = best_path->path.parent->relid; Plan *bitmapqualplan; @@ -3189,7 +3397,7 @@ create_bitmap_scan_plan(PlannerInfo *root, /* Process the bitmapqual tree into a Plan tree and qual lists */ bitmapqualplan = create_bitmap_subplan(root, best_path->bitmapqual, &bitmapqualorig, &indexquals, - &indexECs); + &indexECs, est_calls); if (best_path->path.parallel_aware) bitmap_subplan_mark_shared(bitmapqualplan); @@ -3273,6 +3481,7 @@ create_bitmap_scan_plan(PlannerInfo *root, baserelid); copy_generic_path_info(&scan_plan->scan.plan, &best_path->path); + scan_plan->scan.plan.est_calls = clamp_row_est(est_calls); return scan_plan; } @@ -3299,7 +3508,8 @@ create_bitmap_scan_plan(PlannerInfo *root, */ static Plan * create_bitmap_subplan(PlannerInfo *root, Path *bitmapqual, - List **qual, List **indexqual, List **indexECs) + List **qual, List **indexqual, List **indexECs, + double est_calls) { Plan *plan; @@ -3328,7 +3538,7 @@ create_bitmap_subplan(PlannerInfo *root, Path *bitmapqual, subplan = create_bitmap_subplan(root, (Path *) lfirst(l), &subqual, &subindexqual, - &subindexEC); + &subindexEC, est_calls); subplans = lappend(subplans, subplan); subquals = list_concat_unique(subquals, subqual); subindexquals = list_concat_unique(subindexquals, subindexqual); @@ -3375,7 +3585,7 @@ create_bitmap_subplan(PlannerInfo *root, Path *bitmapqual, subplan = create_bitmap_subplan(root, (Path *) lfirst(l), &subqual, &subindexqual, - &subindexEC); + &subindexEC, est_calls); subplans = lappend(subplans, subplan); if (subqual == NIL) const_true_subqual = true; @@ -3440,7 +3650,7 @@ create_bitmap_subplan(PlannerInfo *root, Path *bitmapqual, /* Use the regular indexscan plan build machinery... */ iscan = castNode(IndexScan, create_indexscan_plan(root, ipath, - NIL, NIL, false)); + NIL, NIL, false, est_calls)); /* then convert to a bitmap indexscan */ plan = (Plan *) make_bitmap_indexscan(iscan->scan.scanrelid, iscan->indexid, @@ -3507,7 +3717,7 @@ create_bitmap_subplan(PlannerInfo *root, Path *bitmapqual, */ static TidScan * create_tidscan_plan(PlannerInfo *root, TidPath *best_path, - List *tlist, List *scan_clauses) + List *tlist, List *scan_clauses, double est_calls) { TidScan *scan_plan; Index scan_relid = best_path->path.parent->relid; @@ -3593,6 +3803,7 @@ create_tidscan_plan(PlannerInfo *root, TidPath *best_path, tidquals); copy_generic_path_info(&scan_plan->scan.plan, &best_path->path); + scan_plan->scan.plan.est_calls = clamp_row_est(est_calls); return scan_plan; } @@ -3604,7 +3815,7 @@ create_tidscan_plan(PlannerInfo *root, TidPath *best_path, */ static TidRangeScan * create_tidrangescan_plan(PlannerInfo *root, TidRangePath *best_path, - List *tlist, List *scan_clauses) + List *tlist, List *scan_clauses, double est_calls) { TidRangeScan *scan_plan; Index scan_relid = best_path->path.parent->relid; @@ -3658,6 +3869,7 @@ create_tidrangescan_plan(PlannerInfo *root, TidRangePath *best_path, tidrangequals); copy_generic_path_info(&scan_plan->scan.plan, &best_path->path); + scan_plan->scan.plan.est_calls = clamp_row_est(est_calls); return scan_plan; } @@ -3669,7 +3881,7 @@ create_tidrangescan_plan(PlannerInfo *root, TidRangePath *best_path, */ static SubqueryScan * create_subqueryscan_plan(PlannerInfo *root, SubqueryScanPath *best_path, - List *tlist, List *scan_clauses) + List *tlist, List *scan_clauses, double est_calls) { SubqueryScan *scan_plan; RelOptInfo *rel = best_path->path.parent; @@ -3685,7 +3897,7 @@ create_subqueryscan_plan(PlannerInfo *root, SubqueryScanPath *best_path, * a different planner context (subroot), recurse to create_plan not * create_plan_recurse. */ - subplan = create_plan(rel->subroot, best_path->subpath); + subplan = create_plan(rel->subroot, best_path->subpath, est_calls); /* Sort clauses into best execution order */ scan_clauses = order_qual_clauses(root, scan_clauses); @@ -3708,6 +3920,7 @@ create_subqueryscan_plan(PlannerInfo *root, SubqueryScanPath *best_path, subplan); copy_generic_path_info(&scan_plan->scan.plan, &best_path->path); + scan_plan->scan.plan.est_calls = clamp_row_est(est_calls); return scan_plan; } @@ -3719,7 +3932,7 @@ create_subqueryscan_plan(PlannerInfo *root, SubqueryScanPath *best_path, */ static FunctionScan * create_functionscan_plan(PlannerInfo *root, Path *best_path, - List *tlist, List *scan_clauses) + List *tlist, List *scan_clauses, double est_calls) { FunctionScan *scan_plan; Index scan_relid = best_path->parent->relid; @@ -3751,6 +3964,7 @@ create_functionscan_plan(PlannerInfo *root, Path *best_path, functions, rte->funcordinality); copy_generic_path_info(&scan_plan->scan.plan, best_path); + scan_plan->scan.plan.est_calls = clamp_row_est(est_calls); return scan_plan; } @@ -3762,7 +3976,7 @@ create_functionscan_plan(PlannerInfo *root, Path *best_path, */ static TableFuncScan * create_tablefuncscan_plan(PlannerInfo *root, Path *best_path, - List *tlist, List *scan_clauses) + List *tlist, List *scan_clauses, double est_calls) { TableFuncScan *scan_plan; Index scan_relid = best_path->parent->relid; @@ -3794,6 +4008,7 @@ create_tablefuncscan_plan(PlannerInfo *root, Path *best_path, tablefunc); copy_generic_path_info(&scan_plan->scan.plan, best_path); + scan_plan->scan.plan.est_calls = clamp_row_est(est_calls); return scan_plan; } @@ -3805,7 +4020,7 @@ create_tablefuncscan_plan(PlannerInfo *root, Path *best_path, */ static ValuesScan * create_valuesscan_plan(PlannerInfo *root, Path *best_path, - List *tlist, List *scan_clauses) + List *tlist, List *scan_clauses, double est_calls) { ValuesScan *scan_plan; Index scan_relid = best_path->parent->relid; @@ -3838,6 +4053,7 @@ create_valuesscan_plan(PlannerInfo *root, Path *best_path, values_lists); copy_generic_path_info(&scan_plan->scan.plan, best_path); + scan_plan->scan.plan.est_calls = clamp_row_est(est_calls); return scan_plan; } @@ -3849,7 +4065,7 @@ create_valuesscan_plan(PlannerInfo *root, Path *best_path, */ static CteScan * create_ctescan_plan(PlannerInfo *root, Path *best_path, - List *tlist, List *scan_clauses) + List *tlist, List *scan_clauses, double est_calls) { CteScan *scan_plan; Index scan_relid = best_path->parent->relid; @@ -3932,6 +4148,7 @@ create_ctescan_plan(PlannerInfo *root, Path *best_path, plan_id, cte_param_id); copy_generic_path_info(&scan_plan->scan.plan, best_path); + scan_plan->scan.plan.est_calls = clamp_row_est(est_calls); return scan_plan; } @@ -3944,7 +4161,8 @@ create_ctescan_plan(PlannerInfo *root, Path *best_path, */ static NamedTuplestoreScan * create_namedtuplestorescan_plan(PlannerInfo *root, Path *best_path, - List *tlist, List *scan_clauses) + List *tlist, List *scan_clauses, + double est_calls) { NamedTuplestoreScan *scan_plan; Index scan_relid = best_path->parent->relid; @@ -3971,6 +4189,7 @@ create_namedtuplestorescan_plan(PlannerInfo *root, Path *best_path, rte->enrname); copy_generic_path_info(&scan_plan->scan.plan, best_path); + scan_plan->scan.plan.est_calls = clamp_row_est(est_calls); return scan_plan; } @@ -3983,7 +4202,7 @@ create_namedtuplestorescan_plan(PlannerInfo *root, Path *best_path, */ static Result * create_resultscan_plan(PlannerInfo *root, Path *best_path, - List *tlist, List *scan_clauses) + List *tlist, List *scan_clauses, double est_calls) { Result *scan_plan; Index scan_relid = best_path->parent->relid; @@ -4009,6 +4228,7 @@ create_resultscan_plan(PlannerInfo *root, Path *best_path, scan_plan = make_result(tlist, (Node *) scan_clauses, NULL); copy_generic_path_info(&scan_plan->plan, best_path); + scan_plan->plan.est_calls = clamp_row_est(est_calls); return scan_plan; } @@ -4020,7 +4240,7 @@ create_resultscan_plan(PlannerInfo *root, Path *best_path, */ static WorkTableScan * create_worktablescan_plan(PlannerInfo *root, Path *best_path, - List *tlist, List *scan_clauses) + List *tlist, List *scan_clauses, double est_calls) { WorkTableScan *scan_plan; Index scan_relid = best_path->parent->relid; @@ -4069,6 +4289,7 @@ create_worktablescan_plan(PlannerInfo *root, Path *best_path, cteroot->wt_param_id); copy_generic_path_info(&scan_plan->scan.plan, best_path); + scan_plan->scan.plan.est_calls = clamp_row_est(est_calls); return scan_plan; } @@ -4080,7 +4301,7 @@ create_worktablescan_plan(PlannerInfo *root, Path *best_path, */ static ForeignScan * create_foreignscan_plan(PlannerInfo *root, ForeignPath *best_path, - List *tlist, List *scan_clauses) + List *tlist, List *scan_clauses, double est_calls) { ForeignScan *scan_plan; RelOptInfo *rel = best_path->path.parent; @@ -4093,7 +4314,7 @@ create_foreignscan_plan(PlannerInfo *root, ForeignPath *best_path, /* transform the child path if any */ if (best_path->fdw_outerpath) outer_plan = create_plan_recurse(root, best_path->fdw_outerpath, - CP_EXACT_TLIST); + CP_EXACT_TLIST, est_calls); /* * If we're scanning a base relation, fetch its OID. (Irrelevant if @@ -4125,10 +4346,11 @@ create_foreignscan_plan(PlannerInfo *root, ForeignPath *best_path, scan_plan = rel->fdwroutine->GetForeignPlan(root, rel, rel_oid, best_path, tlist, scan_clauses, - outer_plan); + outer_plan, est_calls); /* Copy cost data from Path to Plan; no need to make FDW do this */ copy_generic_path_info(&scan_plan->scan.plan, &best_path->path); + scan_plan->scan.plan.est_calls = clamp_row_est(est_calls); /* Copy foreign server OID; likewise, no need to make FDW do this */ scan_plan->fs_server = rel->serverid; @@ -4224,7 +4446,7 @@ create_foreignscan_plan(PlannerInfo *root, ForeignPath *best_path, */ static CustomScan * create_customscan_plan(PlannerInfo *root, CustomPath *best_path, - List *tlist, List *scan_clauses) + List *tlist, List *scan_clauses, double est_calls) { CustomScan *cplan; RelOptInfo *rel = best_path->path.parent; @@ -4235,7 +4457,7 @@ create_customscan_plan(PlannerInfo *root, CustomPath *best_path, foreach(lc, best_path->custom_paths) { Plan *plan = create_plan_recurse(root, (Path *) lfirst(lc), - CP_EXACT_TLIST); + CP_EXACT_TLIST, est_calls); custom_plans = lappend(custom_plans, plan); } @@ -4263,6 +4485,7 @@ create_customscan_plan(PlannerInfo *root, CustomPath *best_path, * do this */ copy_generic_path_info(&cplan->scan.plan, &best_path->path); + cplan->scan.plan.est_calls = clamp_row_est(est_calls); /* Likewise, copy the relids that are represented by this custom scan */ cplan->custom_relids = best_path->path.parent->relids; @@ -4295,7 +4518,7 @@ create_customscan_plan(PlannerInfo *root, CustomPath *best_path, static NestLoop * create_nestloop_plan(PlannerInfo *root, - NestPath *best_path) + NestPath *best_path, double est_calls) { NestLoop *join_plan; Plan *outer_plan; @@ -4309,13 +4532,15 @@ create_nestloop_plan(PlannerInfo *root, Relids saveOuterRels = root->curOuterRels; /* NestLoop can project, so no need to be picky about child tlists */ - outer_plan = create_plan_recurse(root, best_path->jpath.outerjoinpath, 0); + outer_plan = create_plan_recurse(root, best_path->jpath.outerjoinpath, 0, + est_calls); /* For a nestloop, include outer relids in curOuterRels for inner side */ root->curOuterRels = bms_union(root->curOuterRels, best_path->jpath.outerjoinpath->parent->relids); - inner_plan = create_plan_recurse(root, best_path->jpath.innerjoinpath, 0); + inner_plan = create_plan_recurse(root, best_path->jpath.innerjoinpath, 0, + est_calls * outer_plan->plan_rows); /* Restore curOuterRels */ bms_free(root->curOuterRels); @@ -4365,13 +4590,14 @@ create_nestloop_plan(PlannerInfo *root, best_path->jpath.inner_unique); copy_generic_path_info(&join_plan->join.plan, &best_path->jpath.path); + join_plan->join.plan.est_calls = clamp_row_est(est_calls); return join_plan; } static MergeJoin * create_mergejoin_plan(PlannerInfo *root, - MergePath *best_path) + MergePath *best_path, double est_calls) { MergeJoin *join_plan; Plan *outer_plan; @@ -4403,10 +4629,12 @@ create_mergejoin_plan(PlannerInfo *root, * necessary. */ outer_plan = create_plan_recurse(root, best_path->jpath.outerjoinpath, - (best_path->outersortkeys != NIL) ? CP_SMALL_TLIST : 0); + (best_path->outersortkeys != NIL) ? CP_SMALL_TLIST : 0, + est_calls); inner_plan = create_plan_recurse(root, best_path->jpath.innerjoinpath, - (best_path->innersortkeys != NIL) ? CP_SMALL_TLIST : 0); + (best_path->innersortkeys != NIL) ? CP_SMALL_TLIST : 0, + est_calls); /* Sort join qual clauses into best execution order */ /* NB: do NOT reorder the mergeclauses */ @@ -4462,7 +4690,7 @@ create_mergejoin_plan(PlannerInfo *root, Relids outer_relids = outer_path->parent->relids; Sort *sort = make_sort_from_pathkeys(outer_plan, best_path->outersortkeys, - outer_relids); + outer_relids, est_calls); label_sort_with_costsize(root, sort, -1.0); outer_plan = (Plan *) sort; @@ -4476,7 +4704,7 @@ create_mergejoin_plan(PlannerInfo *root, Relids inner_relids = inner_path->parent->relids; Sort *sort = make_sort_from_pathkeys(inner_plan, best_path->innersortkeys, - inner_relids); + inner_relids, est_calls); label_sort_with_costsize(root, sort, -1.0); inner_plan = (Plan *) sort; @@ -4499,6 +4727,7 @@ create_mergejoin_plan(PlannerInfo *root, * sync with final_cost_mergejoin.) */ copy_plan_costsize(matplan, inner_plan); + inner_plan->est_calls = clamp_row_est(est_calls); matplan->total_cost += cpu_operator_cost * matplan->plan_rows; inner_plan = matplan; @@ -4672,13 +4901,14 @@ create_mergejoin_plan(PlannerInfo *root, /* Costs of sort and material steps are included in path cost already */ copy_generic_path_info(&join_plan->join.plan, &best_path->jpath.path); + join_plan->join.plan.est_calls = clamp_row_est(est_calls); return join_plan; } static HashJoin * create_hashjoin_plan(PlannerInfo *root, - HashPath *best_path) + HashPath *best_path, double est_calls) { HashJoin *join_plan; Hash *hash_plan; @@ -4705,10 +4935,11 @@ create_hashjoin_plan(PlannerInfo *root, * that we don't put extra data in the outer batch files. */ outer_plan = create_plan_recurse(root, best_path->jpath.outerjoinpath, - (best_path->num_batches > 1) ? CP_SMALL_TLIST : 0); + (best_path->num_batches > 1) ? CP_SMALL_TLIST : 0, + est_calls); inner_plan = create_plan_recurse(root, best_path->jpath.innerjoinpath, - CP_SMALL_TLIST); + CP_SMALL_TLIST, est_calls); /* Sort join qual clauses into best execution order */ joinclauses = order_qual_clauses(root, best_path->jpath.joinrestrictinfo); @@ -4845,6 +5076,7 @@ create_hashjoin_plan(PlannerInfo *root, best_path->jpath.inner_unique); copy_generic_path_info(&join_plan->join.plan, &best_path->jpath.path); + join_plan->join.plan.est_calls = clamp_row_est(est_calls); return join_plan; } @@ -6106,7 +6338,8 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys, AttrNumber **p_sortColIdx, Oid **p_sortOperators, Oid **p_collations, - bool **p_nullsFirst) + bool **p_nullsFirst, + double est_calls) { List *tlist = lefttree->targetlist; ListCell *i; @@ -6226,7 +6459,8 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys, /* copy needed so we don't modify input's tlist below */ tlist = copyObject(tlist); lefttree = inject_projection_plan(lefttree, tlist, - lefttree->parallel_safe); + lefttree->parallel_safe, + est_calls); } /* Don't bother testing is_projection_capable_plan again */ @@ -6283,7 +6517,8 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys, * 'relids' is the set of relations required by prepare_sort_from_pathkeys() */ static Sort * -make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids) +make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids, + double est_calls) { int numsortkeys; AttrNumber *sortColIdx; @@ -6300,7 +6535,8 @@ make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids) &sortColIdx, &sortOperators, &collations, - &nullsFirst); + &nullsFirst, + est_calls); /* Now build the Sort node */ return make_sort(lefttree, numsortkeys, @@ -6319,7 +6555,8 @@ make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids) */ static IncrementalSort * make_incrementalsort_from_pathkeys(Plan *lefttree, List *pathkeys, - Relids relids, int nPresortedCols) + Relids relids, int nPresortedCols, + double est_calls) { int numsortkeys; AttrNumber *sortColIdx; @@ -6336,7 +6573,8 @@ make_incrementalsort_from_pathkeys(Plan *lefttree, List *pathkeys, &sortColIdx, &sortOperators, &collations, - &nullsFirst); + &nullsFirst, + est_calls); /* Now build the Sort node */ return make_incrementalsort(lefttree, numsortkeys, nPresortedCols, diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index 9a4accb4d9..7b2552c0e4 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -314,6 +314,7 @@ standard_planner(Query *parse, const char *query_string, int cursorOptions, glob->lastPHId = 0; glob->lastRowMarkId = 0; glob->lastPlanNodeId = 0; + glob->jitFlags = PGJIT_NONE; glob->transientPlan = false; glob->dependsOnRole = false; @@ -410,7 +411,7 @@ standard_planner(Query *parse, const char *query_string, int cursorOptions, final_rel = fetch_upper_rel(root, UPPERREL_FINAL, NULL); best_path = get_cheapest_fractional_path(final_rel, tuple_fraction); - top_plan = create_plan(root, best_path); + top_plan = create_plan(root, best_path, 1.0); /* * If creating a plan for a scrollable cursor, make sure it can run @@ -531,32 +532,7 @@ standard_planner(Query *parse, const char *query_string, int cursorOptions, result->utilityStmt = parse->utilityStmt; result->stmt_location = parse->stmt_location; result->stmt_len = parse->stmt_len; - - result->jitFlags = PGJIT_NONE; - if (jit_enabled && jit_above_cost >= 0 && - top_plan->total_cost > jit_above_cost) - { - result->jitFlags |= PGJIT_PERFORM; - - /* - * Decide how much effort should be put into generating better code. - */ - if (jit_optimize_above_cost >= 0 && - top_plan->total_cost > jit_optimize_above_cost) - result->jitFlags |= PGJIT_OPT3; - if (jit_inline_above_cost >= 0 && - top_plan->total_cost > jit_inline_above_cost) - result->jitFlags |= PGJIT_INLINE; - - /* - * Decide which operations should be JITed. - */ - if (jit_expressions) - result->jitFlags |= PGJIT_EXPR; - if (jit_tuple_deforming) - result->jitFlags |= PGJIT_DEFORM; - } - + result->jitFlags = glob->jitFlags; if (glob->partition_directory != NULL) DestroyPartitionDirectory(glob->partition_directory); diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c index df4ca12919..b81d4a1828 100644 --- a/src/backend/optimizer/plan/subselect.c +++ b/src/backend/optimizer/plan/subselect.c @@ -233,7 +233,11 @@ make_subplan(PlannerInfo *root, Query *orig_subquery, final_rel = fetch_upper_rel(subroot, UPPERREL_FINAL, NULL); best_path = get_cheapest_fractional_path(final_rel, tuple_fraction); - plan = create_plan(subroot, best_path); + /* + * XXX we can't get an accurate est_calls to pass to create_plan here as + * we've not yet planned the outer query! + */ + plan = create_plan(subroot, best_path, 1.0); /* And convert to SubPlan or InitPlan format. */ result = build_subplan(root, plan, subroot, plan_params, @@ -284,7 +288,7 @@ make_subplan(PlannerInfo *root, Query *orig_subquery, AlternativeSubPlan *asplan; /* OK, finish planning the ANY subquery */ - plan = create_plan(subroot, best_path); + plan = create_plan(subroot, best_path, 1.0); /* ... and convert to SubPlan format */ hashplan = castNode(SubPlan, @@ -997,7 +1001,7 @@ SS_process_ctes(PlannerInfo *root) final_rel = fetch_upper_rel(subroot, UPPERREL_FINAL, NULL); best_path = final_rel->cheapest_total_path; - plan = create_plan(subroot, best_path); + plan = create_plan(subroot, best_path, 1.0); /* * Make a SubPlan node for it. This is just enough unlike diff --git a/src/include/foreign/fdwapi.h b/src/include/foreign/fdwapi.h index 57c02bff45..9c57896de9 100644 --- a/src/include/foreign/fdwapi.h +++ b/src/include/foreign/fdwapi.h @@ -38,7 +38,8 @@ typedef ForeignScan *(*GetForeignPlan_function) (PlannerInfo *root, ForeignPath *best_path, List *tlist, List *scan_clauses, - Plan *outer_plan); + Plan *outer_plan, + double est_calls); typedef void (*BeginForeignScan_function) (ForeignScanState *node, int eflags); diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h index 244d1e1197..6e78b06f10 100644 --- a/src/include/nodes/pathnodes.h +++ b/src/include/nodes/pathnodes.h @@ -119,6 +119,8 @@ typedef struct PlannerGlobal int lastPlanNodeId; /* highest plan node ID assigned */ + int jitFlags; /* OR mask of jitFlags for each plan node */ + bool transientPlan; /* redo plan when TransactionXmin changes? */ bool dependsOnRole; /* is plan specific to current role? */ @@ -1533,6 +1535,8 @@ typedef struct MemoizePath uint32 est_entries; /* The maximum number of entries that the * planner expects will fit in the cache, or 0 * if unknown */ + double est_hitratio; /* An estimate on the ratio of how many calls + * will result in a cache hit. */ } MemoizePath; /* diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h index e43e360d9b..1ea4c78bcb 100644 --- a/src/include/nodes/plannodes.h +++ b/src/include/nodes/plannodes.h @@ -118,6 +118,9 @@ typedef struct Plan Cost startup_cost; /* cost expended before fetching any tuples */ Cost total_cost; /* total cost (assuming all tuples fetched) */ + double est_calls; /* estimated number of times this plan will be + * (re)scanned */ + /* * planner's estimate of result size of this plan step */ @@ -135,6 +138,11 @@ typedef struct Plan */ bool async_capable; /* engage asynchronous-capable logic? */ + /* + * information needed for jit + */ + bool jit; /* jit compile for this plan node? */ + /* * Common structural data for all Plan types. */ diff --git a/src/include/optimizer/planmain.h b/src/include/optimizer/planmain.h index c4f61c1a09..4b554d2a98 100644 --- a/src/include/optimizer/planmain.h +++ b/src/include/optimizer/planmain.h @@ -38,13 +38,15 @@ extern void preprocess_minmax_aggregates(PlannerInfo *root); /* * prototypes for plan/createplan.c */ -extern Plan *create_plan(PlannerInfo *root, Path *best_path); +extern Plan *create_plan(PlannerInfo *root, Path *best_path, + double est_calls); extern ForeignScan *make_foreignscan(List *qptlist, List *qpqual, Index scanrelid, List *fdw_exprs, List *fdw_private, List *fdw_scan_tlist, List *fdw_recheck_quals, Plan *outer_plan); extern Plan *change_plan_targetlist(Plan *subplan, List *tlist, - bool tlist_parallel_safe); + bool tlist_parallel_safe, + double est_calls); extern Plan *materialize_finished_plan(Plan *subplan); extern bool is_projection_capable_path(Path *path); extern bool is_projection_capable_plan(Plan *plan); diff --git a/src/test/regress/expected/explain.out b/src/test/regress/expected/explain.out index 48620edbc2..416dedee9a 100644 --- a/src/test/regress/expected/explain.out +++ b/src/test/regress/expected/explain.out @@ -100,6 +100,7 @@ select explain_filter('explain (analyze, buffers, format xml) select * from int8 N.N + N + N + + N + N.N + N.N + N + @@ -148,6 +149,7 @@ select explain_filter('explain (analyze, buffers, format yaml) select * from int Total Cost: N.N + Plan Rows: N + Plan Width: N + + Plan Calls: N + Actual Startup Time: N.N + Actual Total Time: N.N + Actual Rows: N + @@ -199,6 +201,7 @@ select explain_filter('explain (buffers, format json) select * from int8_tbl i8' "Total Cost": N.N, + "Plan Rows": N, + "Plan Width": N, + + "Plan Calls": N, + "Shared Hit Blocks": N, + "Shared Read Blocks": N, + "Shared Dirtied Blocks": N, + @@ -244,6 +247,7 @@ select explain_filter('explain (analyze, buffers, format json) select * from int "Total Cost": N.N, + "Plan Rows": N, + "Plan Width": N, + + "Plan Calls": N, + "Actual Startup Time": N.N, + "Actual Total Time": N.N, + "Actual Rows": N, + @@ -363,6 +367,7 @@ select jsonb_pretty( "Schema": "public", + "Node Type": "Seq Scan", + "Plan Rows": 0, + + "Plan Calls": 0, + "Plan Width": 0, + "Total Cost": 0.0, + "Actual Rows": 0, + @@ -409,6 +414,7 @@ select jsonb_pretty( ], + "Node Type": "Sort", + "Plan Rows": 0, + + "Plan Calls": 0, + "Plan Width": 0, + "Total Cost": 0.0, + "Actual Rows": 0, + @@ -452,6 +458,7 @@ select jsonb_pretty( ], + "Node Type": "Gather Merge", + "Plan Rows": 0, + + "Plan Calls": 0, + "Plan Width": 0, + "Total Cost": 0.0, + "Actual Rows": 0, +