From 9e57ff3e8e9054f99a6ee5639f5bd6b95ace0201 Mon Sep 17 00:00:00 2001 From: lizhen Date: Sun, 26 Feb 2023 22:19:23 -0800 Subject: [PATCH] Inner Unique --- src/bin/gs_guc/cluster_guc.conf | 1 + src/common/backend/nodes/copyfuncs.cpp | 2 + src/common/backend/nodes/outfuncs.cpp | 12 ++ src/common/backend/nodes/readfuncs.cpp | 6 + src/common/backend/parser/parse_hint.cpp | 3 +- src/common/backend/utils/init/globals.cpp | 3 +- src/common/backend/utils/misc/guc/guc_sql.cpp | 12 ++ .../utils/misc/postgresql_single.conf.sample | 1 + .../optimizer/commands/explain.cpp | 17 ++ src/gausskernel/optimizer/path/costsize.cpp | 116 ++++++----- src/gausskernel/optimizer/path/joinpath.cpp | 155 +++++++------- .../optimizer/path/streampath_base.cpp | 38 ++-- .../optimizer/path/streampath_single.cpp | 4 +- .../optimizer/plan/analyzejoins.cpp | 75 +++++++ src/gausskernel/optimizer/plan/createplan.cpp | 27 ++- src/gausskernel/optimizer/util/pathnode.cpp | 189 ++++++++++-------- src/gausskernel/optimizer/util/relnode.cpp | 4 + .../runtime/executor/nodeHashjoin.cpp | 7 +- .../runtime/executor/nodeMergejoin.cpp | 57 ++---- .../runtime/executor/nodeNestloop.cpp | 9 +- .../knl/knl_guc/knl_session_attr_sql.h | 1 + src/include/miscadmin.h | 1 + src/include/nodes/execnodes.h | 3 + src/include/nodes/plannodes.h | 3 + src/include/nodes/relation.h | 20 +- src/include/optimizer/cost.h | 12 +- src/include/optimizer/pathnode.h | 6 +- src/include/optimizer/planmain.h | 2 + src/include/optimizer/streampath.h | 17 +- .../regress/expected/hw_subpartition_scan.out | 2 + src/test/regress/expected/multi_delete.out | 3 + src/test/regress/expected/multi_update.out | 3 + .../regress/output/recovery_2pc_tools.source | 1 + 33 files changed, 500 insertions(+), 312 deletions(-) diff --git a/src/bin/gs_guc/cluster_guc.conf b/src/bin/gs_guc/cluster_guc.conf index 592411671..7ba3d81c4 100755 --- a/src/bin/gs_guc/cluster_guc.conf +++ b/src/bin/gs_guc/cluster_guc.conf @@ -199,6 +199,7 @@ enable_hdfs_predicate_pushdown|bool|0,0|NULL|NULL| enable_hypo_index|bool|0,0|NULL|NULL| enable_indexonlyscan|bool|0,0|NULL|NULL| enable_indexscan|bool|0,0|NULL|NULL| +enable_inner_unique_opt|bool|0,0|NULL|NULL| enable_kill_query|bool|0,0|NULL|NULL| enable_material|bool|0,0|NULL|NULL| enable_memory_limit|bool|0,0|NULL|NULL| diff --git a/src/common/backend/nodes/copyfuncs.cpp b/src/common/backend/nodes/copyfuncs.cpp index 82012ad8e..a1e09c7ed 100644 --- a/src/common/backend/nodes/copyfuncs.cpp +++ b/src/common/backend/nodes/copyfuncs.cpp @@ -1138,6 +1138,7 @@ static void CopyJoinFields(const Join* from, Join* newnode) CopyPlanFields((const Plan*)from, (Plan*)newnode); COPY_SCALAR_FIELD(jointype); + COPY_SCALAR_FIELD(inner_unique); COPY_NODE_FIELD(joinqual); COPY_SCALAR_FIELD(optimizable); COPY_NODE_FIELD(nulleqqual); @@ -1217,6 +1218,7 @@ static MergeJoin* _copyMergeJoin(const MergeJoin* from) /* * copy remainder of node */ + COPY_SCALAR_FIELD(skip_mark_restore); COPY_NODE_FIELD(mergeclauses); numCols = list_length(from->mergeclauses); if (numCols > 0) { diff --git a/src/common/backend/nodes/outfuncs.cpp b/src/common/backend/nodes/outfuncs.cpp index ff90ad0f3..258c7af10 100755 --- a/src/common/backend/nodes/outfuncs.cpp +++ b/src/common/backend/nodes/outfuncs.cpp @@ -774,6 +774,9 @@ static void _outJoinPlanInfo(StringInfo str, Join* node) _outPlanInfo(str, (Plan*)node); WRITE_ENUM_FIELD(jointype, JoinType); + if (t_thrd.proc->workingVersionNum >= INNER_UNIQUE_VERSION_NUM) { + WRITE_BOOL_FIELD(inner_unique); + } WRITE_NODE_FIELD(joinqual); WRITE_BOOL_FIELD(optimizable); WRITE_NODE_FIELD(nulleqqual); @@ -1608,6 +1611,9 @@ static void _outCommonJoinPart(StringInfo str, T* node) _outJoinPlanInfo(str, (Join*)node); + if (t_thrd.proc->workingVersionNum >= INNER_UNIQUE_VERSION_NUM) { + WRITE_BOOL_FIELD(skip_mark_restore); + } WRITE_NODE_FIELD(mergeclauses); numCols = list_length(node->mergeclauses); @@ -3050,6 +3056,9 @@ static void _outJoinPathInfo(StringInfo str, JoinPath* node) _outPathInfo(str, (Path*)node); WRITE_ENUM_FIELD(jointype, JoinType); + if (t_thrd.proc->workingVersionNum >= INNER_UNIQUE_VERSION_NUM) { + WRITE_BOOL_FIELD(inner_unique); + } WRITE_NODE_FIELD(outerjoinpath); WRITE_NODE_FIELD(innerjoinpath); WRITE_NODE_FIELD(joinrestrictinfo); @@ -3242,6 +3251,9 @@ static void _outMergePath(StringInfo str, MergePath* node) WRITE_NODE_FIELD(path_mergeclauses); WRITE_NODE_FIELD(outersortkeys); WRITE_NODE_FIELD(innersortkeys); + if (t_thrd.proc->workingVersionNum >= INNER_UNIQUE_VERSION_NUM) { + WRITE_BOOL_FIELD(skip_mark_restore); + } WRITE_BOOL_FIELD(materialize_inner); } diff --git a/src/common/backend/nodes/readfuncs.cpp b/src/common/backend/nodes/readfuncs.cpp index 498f7a548..049619392 100755 --- a/src/common/backend/nodes/readfuncs.cpp +++ b/src/common/backend/nodes/readfuncs.cpp @@ -860,6 +860,9 @@ THR_LOCAL bool skip_read_extern_fields = false; /* Read Join */ \ _readJoin(&local_node->join); \ \ + IF_EXIST(skip_mark_restore) { \ + READ_BOOL_FIELD(skip_mark_restore);\ + } \ READ_NODE_FIELD(mergeclauses); \ LIST_LENGTH(mergeclauses); \ READ_OID_ARRAY_LEN(mergeFamilies); \ @@ -4367,6 +4370,9 @@ static Join* _readJoin(Join* local_node) _readPlan(&local_node->plan); READ_ENUM_FIELD(jointype, JoinType); + IF_EXIST(inner_unique) { + READ_BOOL_FIELD(inner_unique); + } READ_NODE_FIELD(joinqual); READ_BOOL_FIELD(optimizable); READ_NODE_FIELD(nulleqqual); diff --git a/src/common/backend/parser/parse_hint.cpp b/src/common/backend/parser/parse_hint.cpp index de7870417..17d4ab87d 100755 --- a/src/common/backend/parser/parse_hint.cpp +++ b/src/common/backend/parser/parse_hint.cpp @@ -3821,7 +3821,7 @@ bool permit_predpush(PlannerInfo *root) return !predpushHint->negative; } -const unsigned int G_NUM_SET_HINT_WHITE_LIST = 38; +const unsigned int G_NUM_SET_HINT_WHITE_LIST = 39; const char* G_SET_HINT_WHITE_LIST[G_NUM_SET_HINT_WHITE_LIST] = { /* keep in the ascending alphabetical order of frequency */ (char*)"best_agg_plan", @@ -3840,6 +3840,7 @@ const char* G_SET_HINT_WHITE_LIST[G_NUM_SET_HINT_WHITE_LIST] = { (char*)"enable_index_nestloop", (char*)"enable_indexonlyscan", (char*)"enable_indexscan", + (char*)"enable_inner_unique_opt", (char*)"enable_material", (char*)"enable_mergejoin", (char*)"enable_nestloop", diff --git a/src/common/backend/utils/init/globals.cpp b/src/common/backend/utils/init/globals.cpp index b835b3e84..8316b40f6 100644 --- a/src/common/backend/utils/init/globals.cpp +++ b/src/common/backend/utils/init/globals.cpp @@ -59,8 +59,9 @@ bool open_join_children = true; bool will_shutdown = false; /* hard-wired binary version number */ -const uint32 GRAND_VERSION_NUM = 92844; +const uint32 GRAND_VERSION_NUM = 92845; +const uint32 INNER_UNIQUE_VERSION_NUM = 92845; const uint32 PARTITION_ENHANCE_VERSION_NUM = 92844; const uint32 SELECT_INTO_FILE_VERSION_NUM = 92844; const uint32 SELECT_INTO_VAR_VERSION_NUM = 92834; diff --git a/src/common/backend/utils/misc/guc/guc_sql.cpp b/src/common/backend/utils/misc/guc/guc_sql.cpp index 326943c6b..3845f066f 100755 --- a/src/common/backend/utils/misc/guc/guc_sql.cpp +++ b/src/common/backend/utils/misc/guc/guc_sql.cpp @@ -1441,6 +1441,18 @@ static void InitSqlConfigureNamesBool() NULL}, #endif + {{"enable_inner_unique_opt", + PGC_USERSET, + NODE_ALL, + QUERY_TUNING_METHOD, + gettext_noop("Enables the inner unique optimization for JOIN."), + NULL}, + &u_sess->attr.attr_sql.enable_inner_unique_opt, + false, + NULL, + NULL, + NULL}, + {{"enable_partition_opfusion", PGC_USERSET, NODE_ALL, diff --git a/src/common/backend/utils/misc/postgresql_single.conf.sample b/src/common/backend/utils/misc/postgresql_single.conf.sample index d31209286..2723947f6 100644 --- a/src/common/backend/utils/misc/postgresql_single.conf.sample +++ b/src/common/backend/utils/misc/postgresql_single.conf.sample @@ -389,6 +389,7 @@ enable_kill_query = off # optional: [on, off], default: off #check_implicit_conversions = off #enable_functional_dependency = off #enable_indexscan_optimization = off +#enable_inner_unique_opt = off #------------------------------------------------------------------------------ # ERROR REPORTING AND LOGGING diff --git a/src/gausskernel/optimizer/commands/explain.cpp b/src/gausskernel/optimizer/commands/explain.cpp index 4c985481b..85fcd3ed5 100755 --- a/src/gausskernel/optimizer/commands/explain.cpp +++ b/src/gausskernel/optimizer/commands/explain.cpp @@ -2524,6 +2524,23 @@ static void ExplainNode( show_plan_execnodes(planstate, es); } + /* unique join */ + switch (nodeTag(plan)) { + case T_NestLoop: + case T_MergeJoin: + case T_HashJoin: + if (es->format != EXPLAIN_FORMAT_TEXT || (es->verbose && ((Join *) plan)->inner_unique)) + ExplainProperty("Inner Unique", ((Join *) plan)->inner_unique?"true":"false", true, es); + if (is_pretty && es->verbose && ((Join *)plan)->inner_unique) { + es->planinfo->m_detailInfo->set_plan_name(); + appendStringInfo(es->planinfo->m_detailInfo->info_str, "Inner Unique: %s\n", + ((Join *)plan)->inner_unique ? "true" : "false"); + } + break; + default: + break; + } + /* quals, sort keys, etc */ switch (nodeTag(plan)) { case T_IndexScan: diff --git a/src/gausskernel/optimizer/path/costsize.cpp b/src/gausskernel/optimizer/path/costsize.cpp index 591e65223..6dfc60d29 100755 --- a/src/gausskernel/optimizer/path/costsize.cpp +++ b/src/gausskernel/optimizer/path/costsize.cpp @@ -2800,11 +2800,10 @@ void cost_limit(Plan* plan, Plan* lefttree, int64 offset_est, int64 count_est) * 'jointype' is the type of join to be performed * 'outer_path' is the outer input to the join * 'inner_path' is the inner input to the join - * 'sjinfo' is extra info about the join for selectivity estimation - * 'semifactors' contains valid data if jointype is SEMI or ANTI + * 'extra' contains miscellaneous information about the join */ void initial_cost_nestloop(PlannerInfo* root, JoinCostWorkspace* workspace, JoinType jointype, Path* outer_path, - Path* inner_path, SpecialJoinInfo* sjinfo, SemiAntiJoinFactors* semifactors, int dop) + Path* inner_path, JoinPathExtraData *extra, int dop) { Cost startup_cost = 0; Cost run_cost = 0; @@ -2841,7 +2840,8 @@ void initial_cost_nestloop(PlannerInfo* root, JoinCostWorkspace* workspace, Join Selectivity inner_scan_frac; /* - * SEMI or ANTI join: executor will stop after first match. + * With a SEMI or ANTI join, or if the innerrel is known unique, the + * executor will stop after the first match. * * For an outer-rel row that has at least one match, we can expect the * inner scan to stop after a fraction 1/(match_count+1) of the inner @@ -2860,8 +2860,8 @@ void initial_cost_nestloop(PlannerInfo* root, JoinCostWorkspace* workspace, Join */ run_cost += inner_run_cost; - outer_matched_rows = rint(outer_path_rows * semifactors->outer_match_frac); - inner_scan_frac = 2.0 / (semifactors->match_count + 1.0); + outer_matched_rows = rint(outer_path_rows * extra->semifactors.outer_match_frac); + inner_scan_frac = 2.0 / (extra->semifactors.match_count + 1.0); /* Add inner run cost for additional outer tuples having matches */ if (outer_matched_rows > 1) @@ -2903,11 +2903,10 @@ void initial_cost_nestloop(PlannerInfo* root, JoinCostWorkspace* workspace, Join * * 'path' is already filled in except for the rows and cost fields * 'workspace' is the result from initial_cost_nestloop - * 'sjinfo' is extra info about the join for selectivity estimation - * 'semifactors' contains valid data if path->jointype is SEMI or ANTI + * 'extra' contains miscellaneous information about the join */ -void final_cost_nestloop(PlannerInfo* root, NestPath* path, JoinCostWorkspace* workspace, SpecialJoinInfo* sjinfo, - SemiAntiJoinFactors* semifactors, bool hasalternative, int dop) +void final_cost_nestloop(PlannerInfo* root, NestPath* path, JoinCostWorkspace* workspace, JoinPathExtraData *extra, + bool hasalternative, int dop) { Path* outer_path = path->outerjoinpath; Path* inner_path = path->innerjoinpath; @@ -2950,7 +2949,8 @@ void final_cost_nestloop(PlannerInfo* root, NestPath* path, JoinCostWorkspace* w Selectivity inner_scan_frac = workspace->inner_scan_frac; /* - * SEMI or ANTI join: executor will stop after first match. + * With a SEMI or ANTI join, or if the innerrel is known unique, the + * executor will stop after the first match. */ /* Compute number of tuples processed (not number emitted!) */ ntuples = outer_matched_rows * inner_path_rows * inner_scan_frac; @@ -3029,13 +3029,13 @@ void final_cost_nestloop(PlannerInfo* root, NestPath* path, JoinCostWorkspace* w * 'inner_path' is the inner input to the join * 'outersortkeys' is the list of sort keys for the outer path * 'innersortkeys' is the list of sort keys for the inner path - * 'sjinfo' is extra info about the join for selectivity estimation + * 'extra' contains miscellaneous information about the join * * Note: outersortkeys and innersortkeys should be NIL if no explicit * sort is needed because the respective source path is already ordered. */ void initial_cost_mergejoin(PlannerInfo* root, JoinCostWorkspace* workspace, JoinType jointype, List* mergeclauses, - Path* outer_path, Path* inner_path, List* outersortkeys, List* innersortkeys, SpecialJoinInfo* sjinfo) + Path* outer_path, Path* inner_path, List* outersortkeys, List* innersortkeys, JoinPathExtraData *extra) { Cost startup_cost = 0; Cost run_cost = 0; @@ -3247,24 +3247,28 @@ void initial_cost_mergejoin(PlannerInfo* root, JoinCostWorkspace* workspace, Joi * final_cost_mergejoin * Final estimate of the cost and result size of a mergejoin path. * - * Unlike other costsize functions, this routine makes one actual decision: - * whether we should materialize the inner path. We do that either because - * the inner path can't support mark/restore, or because it's cheaper to - * use an interposed Material node to handle mark/restore. When the decision - * is cost-based it would be logically cleaner to build and cost two separate - * paths with and without that flag set; but that would require repeating most - * of the cost calculations, which are not all that cheap. Since the choice - * will not affect output pathkeys or startup cost, only total cost, there is - * no possibility of wanting to keep both paths. So it seems best to make - * the decision here and record it in the path's materialize_inner field. + * Unlike other costsize functions, this routine makes two actual decisions: + * whether the executor will need to do mark/restore, and whether we should + * materialize the inner path. It would be logically cleaner to build + * separate paths testing these alternatives, but that would require repeating + * most of the cost calculations, which are not all that cheap. Since the + * choice will not affect output pathkeys or startup cost, only total cost, + * there is no possibility of wanting to keep more than one path. So it seems + * best to make the decisions here and record them in the path's + * skip_mark_restore and materialize_inner fields. + * + * Mark/restore overhead is usually required, but can be skipped if we know + * that the executor need find only one match per outer tuple, and that the + * mergeclauses are sufficient to identify a match. + * * * 'path' is already filled in except for the rows and cost fields and - * materialize_inner + * skip_mark_restore and materialize_inner * 'workspace' is the result from initial_cost_mergejoin - * 'sjinfo' is extra info about the join for selectivity estimation + * 'extra' contains miscellaneous information about the join */ void final_cost_mergejoin( - PlannerInfo* root, MergePath* path, JoinCostWorkspace* workspace, SpecialJoinInfo* sjinfo, bool hasalternative) + PlannerInfo* root, MergePath* path, JoinCostWorkspace* workspace, JoinPathExtraData *extra, bool hasalternative) { Path* outer_path = path->jpath.outerjoinpath; Path* inner_path = path->jpath.innerjoinpath; @@ -3313,7 +3317,22 @@ void final_cost_mergejoin( cost_qual_eval(&qp_qual_cost, path->jpath.joinrestrictinfo, root); qp_qual_cost.startup -= merge_qual_cost.startup; qp_qual_cost.per_tuple -= merge_qual_cost.per_tuple; - + + /* + * With a SEMI or ANTI join, or if the innerrel is known unique, the + * executor will stop scanning for matches after the first match. When + * all the joinclauses are merge clauses, this means we don't ever need to + * back up the merge, and so we can skip mark/restore overhead. + */ + if (u_sess->attr.attr_sql.enable_inner_unique_opt) { + if ((path->jpath.jointype == JOIN_SEMI || path->jpath.jointype == JOIN_ANTI || extra->inner_unique) && + (list_length(path->jpath.joinrestrictinfo) == list_length(path->path_mergeclauses))) + path->skip_mark_restore = true; + else + path->skip_mark_restore = false; + } else { + path->skip_mark_restore = false; + } /* * Get approx # tuples passing the mergequals. We use approx_tuple_count * here because we need an estimate done with JOIN_INNER semantics. @@ -3554,11 +3573,10 @@ MergeScanSelCache* cached_scansel(PlannerInfo* root, RestrictInfo* rinfo, PathKe * 'hashclauses' is the list of joinclauses to be used as hash clauses * 'outer_path' is the outer input to the join * 'inner_path' is the inner input to the join - * 'sjinfo' is extra info about the join for selectivity estimation - * 'semifactors' contains valid data if jointype is SEMI or ANTI + * 'extra' contains miscellaneous information about the join */ void initial_cost_hashjoin(PlannerInfo* root, JoinCostWorkspace* workspace, JoinType jointype, List* hashclauses, - Path* outer_path, Path* inner_path, SpecialJoinInfo* sjinfo, SemiAntiJoinFactors* semifactors, int dop) + Path* outer_path, Path* inner_path, JoinPathExtraData *extra, int dop) { Cost startup_cost = 0; Cost run_cost = 0; @@ -3816,11 +3834,10 @@ Selectivity compute_bucket_size(PlannerInfo* root, RestrictInfo* restrictinfo, d * 'path' is already filled in except for the rows and cost fields and * num_batches * 'workspace' is the result from initial_cost_hashjoin - * 'sjinfo' is extra info about the join for selectivity estimation - * 'semifactors' contains valid data if path->jointype is SEMI or ANTI + * 'extra' contains miscellaneous information about the join */ -void final_cost_hashjoin(PlannerInfo* root, HashPath* path, JoinCostWorkspace* workspace, SpecialJoinInfo* sjinfo, - SemiAntiJoinFactors* semifactors, bool hasalternative, int dop) +void final_cost_hashjoin(PlannerInfo* root, HashPath* path, JoinCostWorkspace* workspace, JoinPathExtraData *extra, + bool hasalternative, int dop) { Path* outer_path = path->jpath.outerjoinpath; Path* inner_path = path->jpath.innerjoinpath; @@ -3897,7 +3914,7 @@ void final_cost_hashjoin(PlannerInfo* root, HashPath* path, JoinCostWorkspace* w "The NULL PlannerInfo is not allowed." "when estimating the cost and result size of a hashjoin path."); (void)es->calculate_selectivity( - root, hashclauses, sjinfo, path->jpath.jointype, &path->jpath, ES_COMPUTEBUCKETSIZE); + root, hashclauses, extra->sjinfo, path->jpath.jointype, &path->jpath, ES_COMPUTEBUCKETSIZE); es->clear(); clauselist = es->unmatched_clause_group; (void)MemoryContextSwitchTo(oldcontext); @@ -3968,7 +3985,7 @@ void final_cost_hashjoin(PlannerInfo* root, HashPath* path, JoinCostWorkspace* w */ if (bms_is_subset(restrictinfo->right_relids, inner_path->parent->relids)) { thisbucketsize = - compute_bucket_size(root, restrictinfo, virtualbuckets, inner_path, false, sjinfo, &innerdistinct); + compute_bucket_size(root, restrictinfo, virtualbuckets, inner_path, false, extra->sjinfo, &innerdistinct); outerkey = get_leftop(restrictinfo->clause); } else { AssertEreport(bms_is_subset(restrictinfo->left_relids, inner_path->parent->relids), @@ -3976,7 +3993,7 @@ void final_cost_hashjoin(PlannerInfo* root, HashPath* path, JoinCostWorkspace* w "The left relids is not subset of the relids of inner path's parent" "when estimating the cost and result size of a hashjoin path."); thisbucketsize = - compute_bucket_size(root, restrictinfo, virtualbuckets, inner_path, true, sjinfo, &innerdistinct); + compute_bucket_size(root, restrictinfo, virtualbuckets, inner_path, true, extra->sjinfo, &innerdistinct); outerkey = get_rightop(restrictinfo->clause); } @@ -4002,7 +4019,7 @@ void final_cost_hashjoin(PlannerInfo* root, HashPath* path, JoinCostWorkspace* w /* When calculating outerdistinct we have to take skew into consideration */ outerbucketsize = - estimate_hash_bucketsize(root, outerkey, virtualbuckets, outer_path, sjinfo, NULL); + estimate_hash_bucketsize(root, outerkey, virtualbuckets, outer_path, extra->sjinfo, NULL); /* * Restrict outerdistinct less than MIN_HASH_BUCKET_SIZE @@ -4092,7 +4109,8 @@ void final_cost_hashjoin(PlannerInfo* root, HashPath* path, JoinCostWorkspace* w Selectivity inner_scan_frac; /* - * SEMI or ANTI join: executor will stop after first match. + * With a SEMI or ANTI join, or if the innerrel is known unique, the + * executor will stop after the first match. * * For an outer-rel row that has at least one match, we can expect the * bucket scan to stop after a fraction 1/(match_count+1) of the @@ -4102,8 +4120,8 @@ void final_cost_hashjoin(PlannerInfo* root, HashPath* path, JoinCostWorkspace* w * to clamp inner_scan_frac to at most 1.0; but since match_count is * at least 1, no such clamp is needed now.) */ - outer_matched_rows = rint(outer_path_rows * semifactors->outer_match_frac); - inner_scan_frac = 2.0 / (semifactors->match_count + 1.0); + outer_matched_rows = rint(outer_path_rows * extra->semifactors.outer_match_frac); + inner_scan_frac = 2.0 / (extra->semifactors.match_count + 1.0); startup_cost += hash_qual_cost.startup; double matching_cost = hash_qual_cost.per_tuple * clamp_row_est(outer_matched_rows * outer_scan_ratio) * @@ -4178,9 +4196,9 @@ void final_cost_hashjoin(PlannerInfo* root, HashPath* path, JoinCostWorkspace* w * N * Hence, N' should be corrected, e.g. N' = min (N', N * d1/d2) */ - outer_matched_rows = rint(outer_path_rows * semifactors->outer_match_frac); + outer_matched_rows = rint(outer_path_rows * extra->semifactors.outer_match_frac); outer_matched_rows = Min(outer_matched_rows, outer_scan_ratio * outer_path_rows); - inner_matched_rows = rint(inner_path_rows * semifactors->match_count); + inner_matched_rows = rint(inner_path_rows * extra->semifactors.match_count); startup_cost += hash_qual_cost.startup; @@ -4787,6 +4805,7 @@ static void get_restriction_qual_cost( * * In a hash or nestloop SEMI/ANTI join, the executor will stop scanning * inner rows as soon as it finds a match to the current outer row. + * The same happens if we have detected the inner rel is unique. * We should therefore adjust some of the cost components for this effect. * This function computes some estimates needed for these adjustments. * These estimates will be the same regardless of the particular paths used @@ -4796,7 +4815,7 @@ static void get_restriction_qual_cost( * Input parameters: * outerrel: outer relation under consideration * innerrel: inner relation under consideration - * jointype: must be JOIN_SEMI or JOIN_ANTI + * jointype: if not JOIN_SEMI or JOIN_ANTI, we assume it's inner_unique * sjinfo: SpecialJoinInfo relevant to this join * restrictlist: join quals * Output parameters: @@ -4812,12 +4831,6 @@ void compute_semi_anti_join_factors(PlannerInfo* root, RelOptInfo* outerrel, Rel List* joinquals = NIL; ListCell* l = NULL; - /* Should only be called in these cases */ - AssertEreport(jointype == JOIN_SEMI || jointype == JOIN_ANTI, - MOD_OPT, - "Only JOIN_SEMI or JOIN_ANTI can be supported" - "when estimating how much of the inner input a SEMI or ANTI join can be expected to scan."); - /* * In an ANTI join, we must ignore clauses that are "pushed down", since * those won't affect the match logic. In a SEMI join, we do not @@ -4863,8 +4876,9 @@ void compute_semi_anti_join_factors(PlannerInfo* root, RelOptInfo* outerrel, Rel nselec = clauselist_selectivity(root, joinquals, 0, JOIN_INNER, &norm_sjinfo); /* Avoid leaking a lot of ListCells */ - if (jointype == JOIN_ANTI) + if (jointype == JOIN_ANTI) { list_free_ext(joinquals); + } /* * jselec can be interpreted as the fraction of outer-rel rows that have diff --git a/src/gausskernel/optimizer/path/joinpath.cpp b/src/gausskernel/optimizer/path/joinpath.cpp index 977c80f6e..b83682ae9 100755 --- a/src/gausskernel/optimizer/path/joinpath.cpp +++ b/src/gausskernel/optimizer/path/joinpath.cpp @@ -43,18 +43,18 @@ #include "optimizer/streamplan.h" #include "pgxc/pgxc.h" #include "parser/parsetree.h" +#include "optimizer/planmain.h" #define PATH_PARAM_BY_REL(path, rel) \ ((path)->param_info && bms_overlap(PATH_REQ_OUTER(path), (rel)->relids)) static void copy_JoinCostWorkspace(JoinCostWorkspace* to, JoinCostWorkspace* from); static void sort_inner_and_outer(PlannerInfo* root, RelOptInfo* joinrel, RelOptInfo* outerrel, RelOptInfo* innerrel, - List* restrictlist, List* mergeclause_list, JoinType jointype, SpecialJoinInfo* sjinfo, Relids param_source_rels); + List* restrictlist, List* mergeclause_list, JoinType jointype, JoinPathExtraData* extra, Relids param_source_rels); static void match_unsorted_outer(PlannerInfo* root, RelOptInfo* joinrel, RelOptInfo* outerrel, RelOptInfo* innerrel, - List* restrictlist, List* mergeclause_list, JoinType jointype, SpecialJoinInfo* sjinfo, - SemiAntiJoinFactors* semifactors, Relids param_source_rels); + List* restrictlist, List* mergeclause_list, JoinType jointype, JoinPathExtraData* extra, Relids param_source_rels); static void hash_inner_and_outer(PlannerInfo* root, RelOptInfo* joinrel, RelOptInfo* outerrel, RelOptInfo* innerrel, - List* restrictlist, JoinType jointype, SpecialJoinInfo* sjinfo, SemiAntiJoinFactors* semifactors, + List* restrictlist, JoinType jointype, JoinPathExtraData* extra, Relids param_source_rels); static List* select_mergejoin_clauses(PlannerInfo* root, RelOptInfo* joinrel, RelOptInfo* outerrel, RelOptInfo* innerrel, List* restrictlist, JoinType jointype, bool* mergejoin_allowed); @@ -121,9 +121,9 @@ void debug1_print_outerrel_and_innerrel(PlannerInfo* root, RelOptInfo* outerrel, void add_paths_to_joinrel(PlannerInfo* root, RelOptInfo* joinrel, RelOptInfo* outerrel, RelOptInfo* innerrel, JoinType jointype, SpecialJoinInfo* sjinfo, List* restrictlist) { + JoinPathExtraData extra; List* mergeclause_list = NIL; bool mergejoin_allowed = true; - SemiAntiJoinFactors semifactors; Relids param_source_rels = NULL; ListCell* lc = NULL; List *mergejoin_hint = u_sess->attr.attr_sql.enable_mergejoin @@ -138,6 +138,27 @@ void add_paths_to_joinrel(PlannerInfo* root, RelOptInfo* joinrel, RelOptInfo* ou /* print relation information in pg_log before looking for path */ debug1_print_outerrel_and_innerrel(root, outerrel, innerrel); + extra.sjinfo = sjinfo; + if (u_sess->attr.attr_sql.enable_inner_unique_opt) { + switch (jointype) { + case JOIN_SEMI: + case JOIN_ANTI: + extra.inner_unique = false; + break; + case JOIN_UNIQUE_INNER: + extra.inner_unique = bms_is_subset(sjinfo->min_lefthand, outerrel->relids); + break; + case JOIN_UNIQUE_OUTER: + extra.inner_unique = innerrel_is_unique(root, outerrel, innerrel, JOIN_INNER, restrictlist); + break; + default: + extra.inner_unique = innerrel_is_unique(root, outerrel, innerrel, jointype, restrictlist); + break; + } + } else { + extra.inner_unique = false; + } + /* * Find potential mergejoin clauses. We can skip this if we are not * interested in doing a mergejoin. However, mergejoin may be our only @@ -153,18 +174,18 @@ void add_paths_to_joinrel(PlannerInfo* root, RelOptInfo* joinrel, RelOptInfo* ou * estimation. These will be the same for all paths. */ if (jointype == JOIN_SEMI || jointype == JOIN_ANTI) - compute_semi_anti_join_factors(root, outerrel, innerrel, jointype, sjinfo, restrictlist, &semifactors); + compute_semi_anti_join_factors(root, outerrel, innerrel, jointype, sjinfo, restrictlist, &extra.semifactors); if (jointype == JOIN_RIGHT_SEMI) { SemiAntiJoinFactors sf; - compute_semi_anti_join_factors(root, outerrel, innerrel, JOIN_SEMI, sjinfo, restrictlist, &semifactors); + compute_semi_anti_join_factors(root, outerrel, innerrel, JOIN_SEMI, sjinfo, restrictlist, &extra.semifactors); compute_semi_anti_join_factors(root, innerrel, outerrel, JOIN_SEMI, sjinfo, restrictlist, &sf); - semifactors.match_count = sf.outer_match_frac; + extra.semifactors.match_count = sf.outer_match_frac; } if (jointype == JOIN_RIGHT_ANTI) { SemiAntiJoinFactors sf; - compute_semi_anti_join_factors(root, outerrel, innerrel, JOIN_ANTI, sjinfo, restrictlist, &semifactors); + compute_semi_anti_join_factors(root, outerrel, innerrel, JOIN_ANTI, sjinfo, restrictlist, &extra.semifactors); compute_semi_anti_join_factors(root, innerrel, outerrel, JOIN_ANTI, sjinfo, restrictlist, &sf); - semifactors.match_count = sf.outer_match_frac; + extra.semifactors.match_count = sf.outer_match_frac; } /* @@ -265,7 +286,7 @@ void add_paths_to_joinrel(PlannerInfo* root, RelOptInfo* joinrel, RelOptInfo* ou */ if (mergejoin_allowed) sort_inner_and_outer( - root, joinrel, outerrel, innerrel, restrictlist, mergeclause_list, jointype, sjinfo, param_source_rels); + root, joinrel, outerrel, innerrel, restrictlist, mergeclause_list, jointype, &extra, param_source_rels); /* * 2. Consider paths where the outer relation need not be explicitly @@ -282,8 +303,7 @@ void add_paths_to_joinrel(PlannerInfo* root, RelOptInfo* joinrel, RelOptInfo* ou restrictlist, mergeclause_list, jointype, - sjinfo, - &semifactors, + &extra, param_source_rels); #ifdef NOT_USED @@ -319,7 +339,7 @@ void add_paths_to_joinrel(PlannerInfo* root, RelOptInfo* joinrel, RelOptInfo* ou */ if (u_sess->attr.attr_sql.enable_hashjoin || jointype == JOIN_FULL || hashjoin_hint != NIL) hash_inner_and_outer( - root, joinrel, outerrel, innerrel, restrictlist, jointype, sjinfo, &semifactors, param_source_rels); + root, joinrel, outerrel, innerrel, restrictlist, jointype, &extra, param_source_rels); #ifdef PGXC /* @@ -353,7 +373,7 @@ void add_paths_to_joinrel(PlannerInfo* root, RelOptInfo* joinrel, RelOptInfo* ou */ if (set_join_pathlist_hook) { set_join_pathlist_hook(root, joinrel, outerrel, innerrel, - jointype, sjinfo, param_source_rels, &semifactors, restrictlist); + jointype, sjinfo, param_source_rels, &extra.semifactors, restrictlist); } } @@ -508,7 +528,7 @@ DistrbutionPreferenceType get_join_distribution_perference_type(RelOptInfo* join * try nestloop path single */ static void TryNestLoopPathSingle(PlannerInfo* root, RelOptInfo* joinrel, JoinType jointype, - SpecialJoinInfo* sjinfo, SemiAntiJoinFactors* semifactors, Path* outerPath, Path* innerPath, + JoinPathExtraData* extra, Path* outerPath, Path* innerPath, Relids requiredOuter,List* restrictClauses, List* pathkeys, JoinCostWorkspace* workspace) { /* try to use partitionwisejoin if need */ @@ -521,8 +541,7 @@ static void TryNestLoopPathSingle(PlannerInfo* root, RelOptInfo* joinrel, JoinTy joinrel, jointype, workspace, - sjinfo, - semifactors, + extra, outerpath, innerpath, restrictClauses, @@ -536,8 +555,7 @@ static void TryNestLoopPathSingle(PlannerInfo* root, RelOptInfo* joinrel, JoinTy joinrel, jointype, workspace, - sjinfo, - semifactors, + extra, outerPath, innerPath, restrictClauses, @@ -554,7 +572,7 @@ static void TryNestLoopPathSingle(PlannerInfo* root, RelOptInfo* joinrel, JoinTy * the joinrel's pathlist via add_path(). */ static void try_nestloop_path(PlannerInfo* root, RelOptInfo* joinrel, JoinType jointype, JoinType save_jointype, - SpecialJoinInfo* sjinfo, SemiAntiJoinFactors* semifactors, Relids param_source_rels, Path* outer_path, + JoinPathExtraData* extra, Relids param_source_rels, Path* outer_path, Path* inner_path, List* restrict_clauses, List* pathkeys) { bool execOnCoords = false; @@ -590,7 +608,7 @@ static void try_nestloop_path(PlannerInfo* root, RelOptInfo* joinrel, JoinType j * Note: smp does not support parameterized paths. */ int max_dop = (required_outer != NULL) ? 1 : u_sess->opt_cxt.query_dop; - initial_cost_nestloop(root, &workspace, jointype, outer_path, inner_path, sjinfo, semifactors, max_dop); + initial_cost_nestloop(root, &workspace, jointype, outer_path, inner_path, extra, max_dop); if (add_path_precheck(joinrel, workspace.startup_cost, workspace.total_cost, pathkeys, required_outer) || add_path_hintcheck(root->parse->hintState, joinrel->relids, outer_path, inner_path, HINT_KEYWORD_NESTLOOP)) { @@ -606,8 +624,7 @@ static void try_nestloop_path(PlannerInfo* root, RelOptInfo* joinrel, JoinType j joinrel, jointype, save_jointype, - sjinfo, - semifactors, + extra, outer_path, inner_path, restrict_clauses, @@ -673,8 +690,7 @@ static void try_nestloop_path(PlannerInfo* root, RelOptInfo* joinrel, JoinType j TryNestLoopPathSingle(root, joinrel, jointype, - sjinfo, - semifactors, + extra, outer_path, inner_path, required_outer, @@ -693,7 +709,7 @@ static void try_nestloop_path(PlannerInfo* root, RelOptInfo* joinrel, JoinType j * try_mergejoin_path for single node */ static void TryMergeJoinPathSingle(PlannerInfo* root, RelOptInfo* joinrel, JoinType jointype, - SpecialJoinInfo* sjinfo, Path* outerPath, Path* innerPath, List* restrictClauses, Relids requiredOuter, + JoinPathExtraData* extra, Path* outerPath, Path* innerPath, List* restrictClauses, Relids requiredOuter, List* pathkeys, List* mergeclauses, List* outersortkeys, List* innersortkeys, JoinCostWorkspace* workspace) { /* try to use partitionwisejoin if need */ @@ -706,7 +722,7 @@ static void TryMergeJoinPathSingle(PlannerInfo* root, RelOptInfo* joinrel, JoinT joinrel, jointype, workspace, - sjinfo, + extra, outerpath, innerpath, restrictClauses, @@ -724,7 +740,7 @@ static void TryMergeJoinPathSingle(PlannerInfo* root, RelOptInfo* joinrel, JoinT joinrel, jointype, workspace, - sjinfo, + extra, outerPath, innerPath, restrictClauses, @@ -772,7 +788,7 @@ static bool TryMergeJoinPreCheck(PlannerInfo* root, Relids paramSourceRels, * the joinrel's pathlist via add_path(). */ static void try_mergejoin_path(PlannerInfo* root, RelOptInfo* joinrel, JoinType jointype, JoinType save_jointype, - SpecialJoinInfo* sjinfo, Relids param_source_rels, Path* outer_path, Path* inner_path, List* restrict_clauses, + JoinPathExtraData *extra, Relids param_source_rels, Path* outer_path, Path* inner_path, List* restrict_clauses, List* pathkeys, List* mergeclauses, List* outersortkeys, List* innersortkeys) { bool execOnCoords = false; @@ -796,7 +812,7 @@ static void try_mergejoin_path(PlannerInfo* root, RelOptInfo* joinrel, JoinType * See comments in try_nestloop_path(). */ initial_cost_mergejoin( - root, &workspace, jointype, mergeclauses, outer_path, inner_path, outersortkeys, innersortkeys, sjinfo); + root, &workspace, jointype, mergeclauses, outer_path, inner_path, outersortkeys, innersortkeys, extra); if (add_path_precheck(joinrel, workspace.startup_cost, workspace.total_cost, pathkeys, required_outer) || add_path_hintcheck(root->parse->hintState, joinrel->relids, outer_path, inner_path, HINT_KEYWORD_MERGEJOIN)) { @@ -811,8 +827,7 @@ static void try_mergejoin_path(PlannerInfo* root, RelOptInfo* joinrel, JoinType joinrel, jointype, save_jointype, - sjinfo, - NULL, + extra, outer_path, inner_path, restrict_clauses, @@ -878,7 +893,7 @@ static void try_mergejoin_path(PlannerInfo* root, RelOptInfo* joinrel, JoinType TryMergeJoinPathSingle(root, joinrel, jointype, - sjinfo, + extra, outer_path, inner_path, restrict_clauses, @@ -896,7 +911,7 @@ static void try_mergejoin_path(PlannerInfo* root, RelOptInfo* joinrel, JoinType } static void TryHashJoinPathSingle(PlannerInfo* root, RelOptInfo* joinrel, JoinType jointype, - SpecialJoinInfo* sjinfo, SemiAntiJoinFactors* semifactors, Path* outerPath, Path* innerPath, + JoinPathExtraData* extra, Path* outerPath, Path* innerPath, List* restrictClauses, List* hashclauses, Relids requiredOuter, JoinCostWorkspace* workspace) { /* try to use partitionwisejoin if need */ @@ -909,8 +924,7 @@ static void TryHashJoinPathSingle(PlannerInfo* root, RelOptInfo* joinrel, JoinTy joinrel, jointype, workspace, - sjinfo, - semifactors, + extra, outerpath, innerpath, restrictClauses, @@ -924,8 +938,7 @@ static void TryHashJoinPathSingle(PlannerInfo* root, RelOptInfo* joinrel, JoinTy joinrel, jointype, workspace, - sjinfo, - semifactors, + extra, outerPath, innerPath, restrictClauses, @@ -942,7 +955,7 @@ static void TryHashJoinPathSingle(PlannerInfo* root, RelOptInfo* joinrel, JoinTy * the joinrel's pathlist via add_path(). */ static void try_hashjoin_path(PlannerInfo* root, RelOptInfo* joinrel, JoinType jointype, JoinType save_jointype, - SpecialJoinInfo* sjinfo, SemiAntiJoinFactors* semifactors, Relids param_source_rels, Path* outer_path, + JoinPathExtraData* extra, Relids param_source_rels, Path* outer_path, Path* inner_path, List* restrict_clauses, List* hashclauses) { bool execOnCoords = false; @@ -974,7 +987,7 @@ static void try_hashjoin_path(PlannerInfo* root, RelOptInfo* joinrel, JoinType j ? 1 : u_sess->opt_cxt.query_dop; initial_cost_hashjoin( - root, &workspace, jointype, hashclauses, outer_path, inner_path, sjinfo, semifactors, max_dop); + root, &workspace, jointype, hashclauses, outer_path, inner_path, extra, max_dop); if (add_path_precheck(joinrel, workspace.startup_cost, workspace.total_cost, NIL, required_outer) || add_path_hintcheck(root->parse->hintState, joinrel->relids, outer_path, inner_path, HINT_KEYWORD_HASHJOIN)) { @@ -989,8 +1002,7 @@ static void try_hashjoin_path(PlannerInfo* root, RelOptInfo* joinrel, JoinType j joinrel, jointype, save_jointype, - sjinfo, - semifactors, + extra, outer_path, inner_path, restrict_clauses, @@ -1052,8 +1064,7 @@ static void try_hashjoin_path(PlannerInfo* root, RelOptInfo* joinrel, JoinType j TryHashJoinPathSingle(root, joinrel, jointype, - sjinfo, - semifactors, + extra, outer_path, inner_path, restrict_clauses, @@ -1109,7 +1120,7 @@ bool clause_sides_match_join(RestrictInfo* rinfo, RelOptInfo* outerrel, RelOptIn * 'param_source_rels' are OK targets for parameterization of result paths */ static void sort_inner_and_outer(PlannerInfo* root, RelOptInfo* joinrel, RelOptInfo* outerrel, RelOptInfo* innerrel, - List* restrictlist, List* mergeclause_list, JoinType jointype, SpecialJoinInfo* sjinfo, Relids param_source_rels) + List* restrictlist, List* mergeclause_list, JoinType jointype, JoinPathExtraData *extra, Relids param_source_rels) { JoinType save_jointype = jointype; List* all_pathkeys = NIL; @@ -1167,11 +1178,11 @@ static void sort_inner_and_outer(PlannerInfo* root, RelOptInfo* joinrel, RelOptI jointype = save_jointype; if (jointype == JOIN_UNIQUE_OUTER) { - outer_path = (Path*)create_unique_path(root, outerrel, outer_path, sjinfo); + outer_path = (Path*)create_unique_path(root, outerrel, outer_path, extra->sjinfo); AssertEreport(outer_path != NULL, MOD_OPT_JOIN, "Outer path is NULL"); jointype = JOIN_INNER; } else if (jointype == JOIN_UNIQUE_INNER) { - inner_path = (Path*)create_unique_path(root, innerrel, inner_path, sjinfo); + inner_path = (Path*)create_unique_path(root, innerrel, inner_path, extra->sjinfo); AssertEreport(inner_path != NULL, MOD_OPT_JOIN, "Inner path is NULL"); jointype = JOIN_INNER; } @@ -1244,7 +1255,7 @@ static void sort_inner_and_outer(PlannerInfo* root, RelOptInfo* joinrel, RelOptI joinrel, jointype, save_jointype, - sjinfo, + extra, param_source_rels, outer_path, inner_path, @@ -1299,8 +1310,7 @@ static void sort_inner_and_outer(PlannerInfo* root, RelOptInfo* joinrel, RelOptI * 'param_source_rels' are OK targets for parameterization of result paths */ static void match_unsorted_outer(PlannerInfo* root, RelOptInfo* joinrel, RelOptInfo* outerrel, RelOptInfo* innerrel, - List* restrictlist, List* mergeclause_list, JoinType jointype, SpecialJoinInfo* sjinfo, - SemiAntiJoinFactors* semifactors, Relids param_source_rels) + List* restrictlist, List* mergeclause_list, JoinType jointype, JoinPathExtraData* extra, Relids param_source_rels) { JoinType save_jointype = jointype; bool nestjoinOK = false; @@ -1388,7 +1398,7 @@ static void match_unsorted_outer(PlannerInfo* root, RelOptInfo* joinrel, RelOptI if (inner_cheapest_total == NULL) return; - inner_cheapest_total = (Path*)create_unique_path(root, innerrel, inner_cheapest_total, sjinfo); + inner_cheapest_total = (Path*)create_unique_path(root, innerrel, inner_cheapest_total, extra->sjinfo); AssertEreport(inner_cheapest_total != NULL, MOD_OPT_JOIN, "inner cheapest path is NULL"); } else if (nestjoinOK && inner_cheapest_total != NULL ) { /* @@ -1434,7 +1444,7 @@ static void match_unsorted_outer(PlannerInfo* root, RelOptInfo* joinrel, RelOptI if (save_jointype == JOIN_UNIQUE_OUTER) { if (outerpath != outer_cheapest_total) continue; - outerpath = (Path*)create_unique_path(root, outerrel, outerpath, sjinfo); + outerpath = (Path*)create_unique_path(root, outerrel, outerpath, extra->sjinfo); AssertEreport(outerpath != NULL, MOD_OPT_JOIN, "outer path is NULL"); } @@ -1454,8 +1464,7 @@ static void match_unsorted_outer(PlannerInfo* root, RelOptInfo* joinrel, RelOptI joinrel, jointype, save_jointype, - sjinfo, - semifactors, + extra, param_source_rels, outerpath, inner_cheapest_total, @@ -1479,8 +1488,7 @@ static void match_unsorted_outer(PlannerInfo* root, RelOptInfo* joinrel, RelOptI joinrel, jointype, save_jointype, - sjinfo, - semifactors, + extra, param_source_rels, outerpath, innerpath, @@ -1496,8 +1504,7 @@ static void match_unsorted_outer(PlannerInfo* root, RelOptInfo* joinrel, RelOptI joinrel, jointype, save_jointype, - sjinfo, - semifactors, + extra, param_source_rels, outerpath, matpath, @@ -1547,7 +1554,7 @@ static void match_unsorted_outer(PlannerInfo* root, RelOptInfo* joinrel, RelOptI joinrel, jointype, save_jointype, - sjinfo, + extra, param_source_rels, outerpath, inner_cheapest_total, @@ -1629,7 +1636,7 @@ static void match_unsorted_outer(PlannerInfo* root, RelOptInfo* joinrel, RelOptI joinrel, jointype, save_jointype, - sjinfo, + extra, param_source_rels, outerpath, innerpath, @@ -1663,7 +1670,7 @@ static void match_unsorted_outer(PlannerInfo* root, RelOptInfo* joinrel, RelOptI joinrel, jointype, save_jointype, - sjinfo, + extra, param_source_rels, outerpath, innerpath, @@ -1707,7 +1714,7 @@ static void match_unsorted_outer(PlannerInfo* root, RelOptInfo* joinrel, RelOptI * 'param_source_rels' are OK targets for parameterization of result paths */ static void hash_inner_and_outer(PlannerInfo* root, RelOptInfo* joinrel, RelOptInfo* outerrel, RelOptInfo* innerrel, - List* restrictlist, JoinType jointype, SpecialJoinInfo* sjinfo, SemiAntiJoinFactors* semifactors, + List* restrictlist, JoinType jointype, JoinPathExtraData* extra, Relids param_source_rels) { JoinType save_jointype = jointype; @@ -1799,15 +1806,14 @@ static void hash_inner_and_outer(PlannerInfo* root, RelOptInfo* joinrel, RelOptI /* Unique-ify if need be; we ignore parameterized possibilities */ if (jointype == JOIN_UNIQUE_OUTER) { - cheapest_total_outer = (Path*)create_unique_path(root, outerrel, cheapest_total_outer, sjinfo); + cheapest_total_outer = (Path*)create_unique_path(root, outerrel, cheapest_total_outer, extra->sjinfo); AssertEreport(cheapest_total_outer != NULL, MOD_OPT_JOIN, "outer cheapest path is NULL"); jointype = JOIN_INNER; try_hashjoin_path(root, joinrel, jointype, save_jointype, - sjinfo, - semifactors, + extra, param_source_rels, cheapest_total_outer, cheapest_total_inner, @@ -1815,15 +1821,14 @@ static void hash_inner_and_outer(PlannerInfo* root, RelOptInfo* joinrel, RelOptI hashclauses); /* no possibility of cheap startup here */ } else if (jointype == JOIN_UNIQUE_INNER) { - cheapest_total_inner = (Path*)create_unique_path(root, innerrel, cheapest_total_inner, sjinfo); + cheapest_total_inner = (Path*)create_unique_path(root, innerrel, cheapest_total_inner, extra->sjinfo); AssertEreport(cheapest_total_inner != NULL, MOD_OPT_JOIN, "inner cheapest path is NULL"); jointype = JOIN_INNER; try_hashjoin_path(root, joinrel, jointype, save_jointype, - sjinfo, - semifactors, + extra, param_source_rels, cheapest_total_outer, cheapest_total_inner, @@ -1835,8 +1840,7 @@ static void hash_inner_and_outer(PlannerInfo* root, RelOptInfo* joinrel, RelOptI joinrel, jointype, save_jointype, - sjinfo, - semifactors, + extra, param_source_rels, cheapest_startup_outer, cheapest_total_inner, @@ -1858,8 +1862,7 @@ static void hash_inner_and_outer(PlannerInfo* root, RelOptInfo* joinrel, RelOptI joinrel, jointype, save_jointype, - sjinfo, - semifactors, + extra, param_source_rels, cheapest_total_outer, cheapest_total_inner, @@ -1873,8 +1876,7 @@ static void hash_inner_and_outer(PlannerInfo* root, RelOptInfo* joinrel, RelOptI joinrel, jointype, save_jointype, - sjinfo, - semifactors, + extra, param_source_rels, cheapest_startup_outer, cheapest_total_inner, @@ -1911,8 +1913,7 @@ static void hash_inner_and_outer(PlannerInfo* root, RelOptInfo* joinrel, RelOptI joinrel, jointype, save_jointype, - sjinfo, - semifactors, + extra, param_source_rels, outerpath, innerpath, diff --git a/src/gausskernel/optimizer/path/streampath_base.cpp b/src/gausskernel/optimizer/path/streampath_base.cpp index 65a224e05..0b7592530 100755 --- a/src/gausskernel/optimizer/path/streampath_base.cpp +++ b/src/gausskernel/optimizer/path/streampath_base.cpp @@ -123,14 +123,13 @@ void PathGen::addPath(Path* new_path) * @param[IN] required_outer: the set of required outer rels. */ JoinPathGenBase::JoinPathGenBase(PlannerInfo* root, RelOptInfo* joinrel, JoinType jointype, JoinType save_jointype, - SpecialJoinInfo* sjinfo, SemiAntiJoinFactors* semifactors, List* joinclauses, List* restrictinfo, + JoinPathExtraData* extra, List* joinclauses, List* restrictinfo, Path* outer_path, Path* inner_path, Relids required_outer) : PathGen(root, joinrel), m_jointype(jointype), m_saveJointype(save_jointype), m_workspace(NULL), - m_sjinfo(sjinfo), - m_semifactors(semifactors), + m_extra(extra), m_joinClauses(joinclauses), m_joinRestrictinfo(restrictinfo), m_pathkeys(NIL), @@ -193,8 +192,6 @@ JoinPathGenBase::~JoinPathGenBase() m_resourceOwner = NULL; m_rrinfoInner = NIL; m_rrinfoOuter = NIL; - m_semifactors = NULL; - m_sjinfo = NULL; m_streamInfoList = NIL; m_streamInfoPair = NULL; m_targetDistribution = NULL; @@ -1023,9 +1020,9 @@ Path* JoinPathGenBase::makeJoinSkewUniquePath(bool stream_outer, List* pathkeys) * @param[IN] required_outer: the set of required outer rels. */ HashJoinPathGen::HashJoinPathGen(PlannerInfo* root, RelOptInfo* joinrel, JoinType jointype, JoinType save_jointype, - SpecialJoinInfo* sjinfo, SemiAntiJoinFactors* semifactors, Path* outer_path, Path* inner_path, List* restrictlist, + JoinPathExtraData* extra, Path* outer_path, Path* inner_path, List* restrictlist, Relids required_outer, List* hashclauses) - : JoinPathGen(root, joinrel, jointype, save_jointype, sjinfo, semifactors, hashclauses, restrictlist, outer_path, + : JoinPathGen(root, joinrel, jointype, save_jointype, extra, hashclauses, restrictlist, outer_path, inner_path, required_outer), m_hashClauses(hashclauses) { @@ -1102,7 +1099,7 @@ Path* HashJoinPathGen::createHashJoinPath() pathnode->jpath.path.pathtype = T_HashJoin; pathnode->jpath.path.parent = m_rel; pathnode->jpath.path.param_info = get_joinrel_parampathinfo( - m_root, m_rel, m_outerStreamPath, m_innerStreamPath, m_sjinfo, m_requiredOuter, &m_joinRestrictinfo); + m_root, m_rel, m_outerStreamPath, m_innerStreamPath, m_extra->sjinfo, m_requiredOuter, &m_joinRestrictinfo); /* * A hashjoin never has pathkeys, since its output ordering is @@ -1170,8 +1167,7 @@ void HashJoinPathGen::initialCostHashjoin() m_hashClauses, m_outerStreamPath, m_innerStreamPath, - m_sjinfo, - m_semifactors, + m_extra, m_dop); } @@ -1186,7 +1182,7 @@ void HashJoinPathGen::initialCostHashjoin() */ void HashJoinPathGen::finalCostHashjoin(HashPath* path, bool hasalternative) { - final_cost_hashjoin(m_root, path, m_workspace, m_sjinfo, m_semifactors, hasalternative, path->jpath.path.dop); + final_cost_hashjoin(m_root, path, m_workspace, m_extra, hasalternative, path->jpath.path.dop); } /* @@ -1205,9 +1201,9 @@ void HashJoinPathGen::finalCostHashjoin(HashPath* path, bool hasalternative) * @param[IN] required_outer: the set of required outer rels. */ NestLoopPathGen::NestLoopPathGen(PlannerInfo* root, RelOptInfo* joinrel, JoinType jointype, JoinType save_jointype, - SpecialJoinInfo* sjinfo, SemiAntiJoinFactors* semifactors, Path* outer_path, Path* inner_path, List* restrictlist, + JoinPathExtraData* extra, Path* outer_path, Path* inner_path, List* restrictlist, List* pathkeys, Relids required_outer) - : JoinPathGen(root, joinrel, jointype, save_jointype, sjinfo, semifactors, restrictlist, restrictlist, outer_path, + : JoinPathGen(root, joinrel, jointype, save_jointype, extra, restrictlist, restrictlist, outer_path, inner_path, required_outer) { m_joinmethod = T_NestLoop; @@ -1287,7 +1283,7 @@ void NestLoopPathGen::addNestloopPathToList() void NestLoopPathGen::initialCostNestloop() { initial_cost_nestloop( - m_root, m_workspace, m_jointype, m_outerStreamPath, m_innerStreamPath, m_sjinfo, m_semifactors, m_dop); + m_root, m_workspace, m_jointype, m_outerStreamPath, m_innerStreamPath, m_extra, m_dop); } /* @@ -1297,7 +1293,7 @@ void NestLoopPathGen::initialCostNestloop() */ void NestLoopPathGen::finalCostNestloop(NestPath* path, bool hasalternative) { - final_cost_nestloop(m_root, path, m_workspace, m_sjinfo, m_semifactors, hasalternative, m_dop); + final_cost_nestloop(m_root, path, m_workspace, m_extra, hasalternative, m_dop); } /* @@ -1359,7 +1355,7 @@ Path* NestLoopPathGen::createNestloopPath() pathnode->path.parent = m_rel; if (m_root != NULL) { pathnode->path.param_info = get_joinrel_parampathinfo( - m_root, m_rel, m_outerStreamPath, m_innerStreamPath, m_sjinfo, m_requiredOuter, &m_joinClauses); + m_root, m_rel, m_outerStreamPath, m_innerStreamPath, m_extra->sjinfo, m_requiredOuter, &m_joinClauses); } pathnode->path.pathkeys = m_pathkeys; if (IsA(m_outerStreamPath, StreamPath) && NIL == m_outerStreamPath->pathkeys) { @@ -1411,10 +1407,10 @@ Path* NestLoopPathGen::createNestloopPath() * @param[IN] required_outer: the set of required outer rels. */ MergeJoinPathGen::MergeJoinPathGen(PlannerInfo* root, RelOptInfo* joinrel, JoinType jointype, JoinType save_jointype, - SpecialJoinInfo* sjinfo, SemiAntiJoinFactors* semifactors, Path* outer_path, Path* inner_path, + JoinPathExtraData* extra, Path* outer_path, Path* inner_path, List* restrict_clauses, List* pathkeys, Relids required_outer, List* mergeclauses, List* outersortkeys, List* innersortkeys) - : JoinPathGen(root, joinrel, jointype, save_jointype, sjinfo, semifactors, restrict_clauses, restrict_clauses, + : JoinPathGen(root, joinrel, jointype, save_jointype, extra, restrict_clauses, restrict_clauses, outer_path, inner_path, required_outer) { m_joinmethod = T_MergeJoin; @@ -1509,7 +1505,7 @@ void MergeJoinPathGen::initialCostMergejoin() m_innerStreamPath, m_outerSortKeys, m_innerSortKeys, - m_sjinfo); + m_extra); } /* @@ -1530,7 +1526,7 @@ void MergeJoinPathGen::initialCostMergejoin() */ void MergeJoinPathGen::finalCostMergejoin(MergePath* path, bool hasalternative) { - final_cost_mergejoin(m_root, path, m_workspace, m_sjinfo, hasalternative); + final_cost_mergejoin(m_root, path, m_workspace, m_extra, hasalternative); } /* @@ -1550,7 +1546,7 @@ Path* MergeJoinPathGen::createMergejoinPath() pathnode->jpath.path.pathtype = T_MergeJoin; pathnode->jpath.path.parent = m_rel; pathnode->jpath.path.param_info = get_joinrel_parampathinfo( - m_root, m_rel, m_outerStreamPath, m_innerStreamPath, m_sjinfo, m_requiredOuter, &m_joinRestrictinfo); + m_root, m_rel, m_outerStreamPath, m_innerStreamPath, m_extra->sjinfo, m_requiredOuter, &m_joinRestrictinfo); pathnode->jpath.path.pathkeys = m_pathkeys; pathnode->jpath.jointype = m_jointype; pathnode->jpath.outerjoinpath = m_outerStreamPath; diff --git a/src/gausskernel/optimizer/path/streampath_single.cpp b/src/gausskernel/optimizer/path/streampath_single.cpp index c1a17fa55..1788eb1a6 100644 --- a/src/gausskernel/optimizer/path/streampath_single.cpp +++ b/src/gausskernel/optimizer/path/streampath_single.cpp @@ -69,9 +69,9 @@ * @param[IN] required_outer: the set of required outer rels. */ JoinPathGen::JoinPathGen(PlannerInfo* root, RelOptInfo* joinrel, JoinType jointype, JoinType save_jointype, - SpecialJoinInfo* sjinfo, SemiAntiJoinFactors* semifactors, List* joinclauses, List* restrictinfo, + JoinPathExtraData* extra, List* joinclauses, List* restrictinfo, Path* outer_path, Path* inner_path, Relids required_outer) - : JoinPathGenBase(root, joinrel, jointype, save_jointype, sjinfo, semifactors, joinclauses, + : JoinPathGenBase(root, joinrel, jointype, save_jointype, extra, joinclauses, restrictinfo, outer_path, inner_path, required_outer) {} diff --git a/src/gausskernel/optimizer/plan/analyzejoins.cpp b/src/gausskernel/optimizer/plan/analyzejoins.cpp index abb6912ee..347333335 100644 --- a/src/gausskernel/optimizer/plan/analyzejoins.cpp +++ b/src/gausskernel/optimizer/plan/analyzejoins.cpp @@ -41,6 +41,8 @@ static bool rel_supports_distinctness(PlannerInfo* root, RelOptInfo* rel); static bool rel_is_distinct_for(PlannerInfo* root, RelOptInfo* rel, List* clause_list); static bool check_column_uniqueness(List* groupClause, List* targetList, List* colnos, List* opids); static Oid distinct_col_search(int colno, List* colnos, List* opids); +static bool is_innerrel_unique_for(PlannerInfo *root, RelOptInfo *outerrel, RelOptInfo *innerrel, JoinType jointype, + List *restrictlist); /* * remove_useless_joins @@ -782,3 +784,76 @@ static Oid distinct_col_search(int colno, List* colnos, List* opids) } return InvalidOid; } + +bool innerrel_is_unique(PlannerInfo *root, RelOptInfo *outerrel, RelOptInfo *innerrel, JoinType jointype, + List *restrictlist) +{ + MemoryContext old_context; + ListCell *lc; + + if (restrictlist == NIL) { + return false; + } + + if (!rel_supports_distinctness(root, innerrel)) { + return false; + } + + foreach (lc, innerrel->unique_for_rels) { + Relids unique_for_rels = (Relids)lfirst(lc); + + if (bms_is_subset(unique_for_rels, outerrel->relids)) { + return true; + } + } + + foreach (lc, innerrel->non_unique_for_rels) { + Relids unique_for_rels = (Relids)lfirst(lc); + + if (bms_is_subset(outerrel->relids, unique_for_rels)) { + return false; + } + } + + if (is_innerrel_unique_for(root, outerrel, innerrel, jointype, restrictlist)) { + old_context = MemoryContextSwitchTo(root->planner_cxt); + innerrel->unique_for_rels = lappend(innerrel->unique_for_rels, bms_copy(outerrel->relids)); + MemoryContextSwitchTo(old_context); + return true; + } else { + if (root->join_search_private) { + old_context = MemoryContextSwitchTo(root->planner_cxt); + innerrel->non_unique_for_rels = lappend(innerrel->non_unique_for_rels, bms_copy(outerrel->relids)); + MemoryContextSwitchTo(old_context); + } + + return false; + } +} + +static bool is_innerrel_unique_for(PlannerInfo *root, RelOptInfo *outerrel, RelOptInfo *innerrel, JoinType jointype, + List *restrictlist) +{ + List *clause_list = NIL; + ListCell *lc; + + foreach (lc, restrictlist) { + RestrictInfo *restrictinfo = (RestrictInfo *)lfirst(lc); + + if (restrictinfo->is_pushed_down && IS_OUTER_JOIN(jointype)) { + continue; + } + + if (!restrictinfo->can_join || restrictinfo->mergeopfamilies == NIL) { + continue; + } + + if (!clause_sides_match_join(restrictinfo, outerrel->relids, innerrel->relids)) { + continue; + } + + clause_list = lappend(clause_list, restrictinfo); + } + + return rel_is_distinct_for(root, innerrel, clause_list); +} diff --git a/src/gausskernel/optimizer/plan/createplan.cpp b/src/gausskernel/optimizer/plan/createplan.cpp index ea5a056f8..8b2e5296f 100755 --- a/src/gausskernel/optimizer/plan/createplan.cpp +++ b/src/gausskernel/optimizer/plan/createplan.cpp @@ -163,14 +163,14 @@ static WorkTableScan* make_worktablescan(List* qptlist, List* qpqual, Index scan static BitmapAnd* make_bitmap_and(List* bitmapplans); static BitmapOr* make_bitmap_or(List* bitmapplans); static NestLoop* make_nestloop(List* tlist, List* joinclauses, List* otherclauses, List* nestParams, Plan* lefttree, - Plan* righttree, JoinType jointype); + Plan* righttree, JoinType jointype, bool inner_unique); static HashJoin* make_hashjoin(List* tlist, List* joinclauses, List* otherclauses, List* hashclauses, Plan* lefttree, - Plan* righttree, JoinType jointype, List *hashcollations); + Plan* righttree, JoinType jointype, List *hashcollations, bool inner_unique); static Hash* make_hash( Plan* lefttree, Oid skewTable, AttrNumber skewColumn, bool skewInherit, Oid skewColType, int32 skewColTypmod); static MergeJoin* make_mergejoin(List* tlist, List* joinclauses, List* otherclauses, List* mergeclauses, Oid* mergefamilies, Oid* mergecollations, int* mergestrategies, bool* mergenullsfirst, Plan* lefttree, - Plan* righttree, JoinType jointype); + Plan* righttree, JoinType jointype, bool inner_unique, bool skip_mark_restore); static Plan* prepare_sort_from_pathkeys(PlannerInfo* root, Plan* lefttree, List* pathkeys, Relids relids, const AttrNumber* reqColIdx, bool adjust_tlist_in_place, int* p_numsortkeys, AttrNumber** p_sortColIdx, Oid** p_sortOperators, Oid** p_collations, bool** p_nullsFirst); @@ -3965,7 +3965,8 @@ static NestLoop* create_nestloop_plan(PlannerInfo* root, NestPath* best_path, Pl #endif join_plan = - make_nestloop(tlist, joinclauses, otherclauses, nestParams, outer_plan, inner_plan, best_path->jointype); + make_nestloop(tlist, joinclauses, otherclauses, nestParams, + outer_plan, inner_plan, best_path->jointype, best_path->inner_unique); /* * @hdfs @@ -4291,7 +4292,9 @@ static MergeJoin* create_mergejoin_plan(PlannerInfo* root, MergePath* best_path, mergenullsfirst, outer_plan, inner_plan, - best_path->jpath.jointype); + best_path->jpath.jointype, + best_path->jpath.inner_unique, + best_path->skip_mark_restore); /* * @hdfs @@ -4719,7 +4722,7 @@ static HashJoin* create_hashjoin_plan(PlannerInfo* root, HashPath* best_path, Pl */ hash_plan = make_hash(inner_plan, skewTable, skewColumn, skewInherit, skewColType, skewColTypmod); join_plan = make_hashjoin(tlist, joinclauses, otherclauses, hashclauses, outer_plan, (Plan*)hash_plan, - best_path->jpath.jointype, hashcollations); + best_path->jpath.jointype, hashcollations, best_path->jpath.inner_unique); /* * @hdfs @@ -6251,7 +6254,7 @@ static CStoreIndexOr* make_cstoreindex_or(List* ctidplans) } static NestLoop* make_nestloop(List* tlist, List* joinclauses, List* otherclauses, List* nestParams, Plan* lefttree, - Plan* righttree, JoinType jointype) + Plan* righttree, JoinType jointype, bool inner_unique) { NestLoop* node = makeNode(NestLoop); Plan* plan = &node->join.plan; @@ -6262,6 +6265,7 @@ static NestLoop* make_nestloop(List* tlist, List* joinclauses, List* otherclause plan->lefttree = lefttree; plan->righttree = righttree; node->join.jointype = jointype; + node->join.inner_unique = inner_unique; node->join.joinqual = joinclauses; node->nestParams = nestParams; @@ -6425,7 +6429,7 @@ HashJoin* create_direct_hashjoin( } hash_plan = (Plan*)make_hash(innerPlan, skewTable, skewColumn, skewInherit, skewColType, skewColTypmod); - join_plan = make_hashjoin(tlist, joinClauses, NIL, hashclauses, outerPlan, hash_plan, joinType, NULL); + join_plan = make_hashjoin(tlist, joinClauses, NIL, hashclauses, outerPlan, hash_plan, joinType, NULL, false); /* estimate the mem_info for join_plan, refered to the function initial_cost_hashjoin */ estimate_directHashjoin_Cost(root, hashclauses, outerPlan, hash_plan, join_plan); @@ -6637,7 +6641,7 @@ Plan* create_direct_righttree( } static HashJoin* make_hashjoin(List* tlist, List* joinclauses, List* otherclauses, List* hashclauses, Plan* lefttree, - Plan* righttree, JoinType jointype, List *hashcollations) + Plan* righttree, JoinType jointype, List *hashcollations, bool inner_unique) { HashJoin* node = makeNode(HashJoin); Plan* plan = &node->join.plan; @@ -6649,6 +6653,7 @@ static HashJoin* make_hashjoin(List* tlist, List* joinclauses, List* otherclause plan->righttree = righttree; node->hashclauses = hashclauses; node->join.jointype = jointype; + node->join.inner_unique = inner_unique; node->join.joinqual = joinclauses; node->hash_collations = hashcollations; @@ -6689,7 +6694,7 @@ static Hash* make_hash( static MergeJoin* make_mergejoin(List* tlist, List* joinclauses, List* otherclauses, List* mergeclauses, Oid* mergefamilies, Oid* mergecollations, int* mergestrategies, bool* mergenullsfirst, Plan* lefttree, - Plan* righttree, JoinType jointype) + Plan* righttree, JoinType jointype, bool inner_unique, bool skip_mark_restore) { MergeJoin* node = makeNode(MergeJoin); Plan* plan = &node->join.plan; @@ -6699,12 +6704,14 @@ static MergeJoin* make_mergejoin(List* tlist, List* joinclauses, List* otherclau plan->qual = otherclauses; plan->lefttree = lefttree; plan->righttree = righttree; + node->skip_mark_restore = skip_mark_restore; node->mergeclauses = mergeclauses; node->mergeFamilies = mergefamilies; node->mergeCollations = mergecollations; node->mergeStrategies = mergestrategies; node->mergeNullsFirst = mergenullsfirst; node->join.jointype = jointype; + node->join.inner_unique = inner_unique; node->join.joinqual = joinclauses; return node; diff --git a/src/gausskernel/optimizer/util/pathnode.cpp b/src/gausskernel/optimizer/util/pathnode.cpp index bbf3525e2..c16c5e376 100755 --- a/src/gausskernel/optimizer/util/pathnode.cpp +++ b/src/gausskernel/optimizer/util/pathnode.cpp @@ -4089,8 +4089,7 @@ bool equivalence_class_overlap(PlannerInfo* root, Relids outer_relids, Relids in * 'joinrel' is the join relation. * 'jointype' is the type of join required * 'workspace' is the result from initial_cost_nestloop - * 'sjinfo' is extra info about the join for selectivity estimation - * 'semifactors' contains valid data if jointype is SEMI or ANTI + * 'extra' contains various information about the join * 'outer_path' is the outer path * 'inner_path' is the inner path * 'restrict_clauses' are the RestrictInfo nodes to apply at the join @@ -4100,7 +4099,7 @@ bool equivalence_class_overlap(PlannerInfo* root, Relids outer_relids, Relids in * Returns the resulting path node. */ NestPath* create_nestloop_path(PlannerInfo* root, RelOptInfo* joinrel, JoinType jointype, JoinCostWorkspace* workspace, - SpecialJoinInfo* sjinfo, SemiAntiJoinFactors* semifactors, Path* outer_path, Path* inner_path, + JoinPathExtraData *extra, Path* outer_path, Path* inner_path, List* restrict_clauses, List* pathkeys, Relids required_outer, int dop) { NestPath* pathnode = makeNode(NestPath); @@ -4181,13 +4180,14 @@ NestPath* create_nestloop_path(PlannerInfo* root, RelOptInfo* joinrel, JoinType pathnode->path.pathtype = T_NestLoop; pathnode->path.parent = joinrel; pathnode->path.param_info = - get_joinrel_parampathinfo(root, joinrel, outer_path, inner_path, sjinfo, required_outer, &restrict_clauses); + get_joinrel_parampathinfo(root, joinrel, outer_path, inner_path, extra->sjinfo, required_outer, &restrict_clauses); pathnode->path.pathkeys = pathkeys; if (IsA(outer_path, StreamPath) && NIL == outer_path->pathkeys) { pathnode->path.pathkeys = NIL; } pathnode->path.dop = dop; pathnode->jointype = jointype; + pathnode->inner_unique = extra->inner_unique; pathnode->outerjoinpath = outer_path; pathnode->innerjoinpath = inner_path; pathnode->joinrestrictinfo = restrict_clauses; @@ -4205,7 +4205,7 @@ NestPath* create_nestloop_path(PlannerInfo* root, RelOptInfo* joinrel, JoinType } #endif - final_cost_nestloop(root, pathnode, workspace, sjinfo, semifactors, hasalternative, dop); + final_cost_nestloop(root, pathnode, workspace, extra, hasalternative, dop); return pathnode; } @@ -4218,7 +4218,7 @@ NestPath* create_nestloop_path(PlannerInfo* root, RelOptInfo* joinrel, JoinType * 'joinrel' is the join relation * 'jointype' is the type of join required * 'workspace' is the result from initial_cost_mergejoin - * 'sjinfo' is extra info about the join for selectivity estimation + * 'extra' contains various information about the join * 'outer_path' is the outer path * 'inner_path' is the inner path * 'restrict_clauses' are the RestrictInfo nodes to apply at the join @@ -4230,7 +4230,7 @@ NestPath* create_nestloop_path(PlannerInfo* root, RelOptInfo* joinrel, JoinType * 'innersortkeys' are the sort varkeys for the inner relation */ MergePath* create_mergejoin_path(PlannerInfo* root, RelOptInfo* joinrel, JoinType jointype, - JoinCostWorkspace* workspace, SpecialJoinInfo* sjinfo, Path* outer_path, Path* inner_path, List* restrict_clauses, + JoinCostWorkspace* workspace, JoinPathExtraData *extra, Path* outer_path, Path* inner_path, List* restrict_clauses, List* pathkeys, Relids required_outer, List* mergeclauses, List* outersortkeys, List* innersortkeys) { MergePath* pathnode = makeNode(MergePath); @@ -4239,9 +4239,10 @@ MergePath* create_mergejoin_path(PlannerInfo* root, RelOptInfo* joinrel, JoinTyp pathnode->jpath.path.pathtype = T_MergeJoin; pathnode->jpath.path.parent = joinrel; pathnode->jpath.path.param_info = - get_joinrel_parampathinfo(root, joinrel, outer_path, inner_path, sjinfo, required_outer, &restrict_clauses); + get_joinrel_parampathinfo(root, joinrel, outer_path, inner_path, extra->sjinfo, required_outer, &restrict_clauses); pathnode->jpath.path.pathkeys = pathkeys; pathnode->jpath.jointype = jointype; + pathnode->jpath.inner_unique = extra->inner_unique; pathnode->jpath.outerjoinpath = outer_path; pathnode->jpath.innerjoinpath = inner_path; pathnode->jpath.joinrestrictinfo = restrict_clauses; @@ -4252,6 +4253,7 @@ MergePath* create_mergejoin_path(PlannerInfo* root, RelOptInfo* joinrel, JoinTyp pathnode->jpath.path.exec_type = SetExectypeForJoinPath(inner_path, outer_path); /* pathnode->materialize_inner will be set by final_cost_mergejoin */ + /* pathnode->skip_mark_restore will be set by final_cost_mergejoin */ #ifdef STREAMPLAN pathnode->jpath.path.locator_type = locator_type_join(outer_path->locator_type, inner_path->locator_type); ProcessRangeListJoinType(&pathnode->jpath.path, outer_path, inner_path); @@ -4267,7 +4269,7 @@ MergePath* create_mergejoin_path(PlannerInfo* root, RelOptInfo* joinrel, JoinTyp final_cost_mergejoin(root, pathnode, workspace, - sjinfo, + extra, check_join_method_alternative( restrict_clauses, outer_path->parent, inner_path->parent, jointype, &try_eq_related_indirectly)); @@ -4281,7 +4283,7 @@ MergePath* create_mergejoin_path(PlannerInfo* root, RelOptInfo* joinrel, JoinTyp * 'joinrel' is the join relation * 'jointype' is the type of join required * 'workspace' is the result from initial_cost_hashjoin - * 'sjinfo' is extra info about the join for selectivity estimation + * 'extra' contains various information about the join * 'semifactors' contains valid data if jointype is SEMI or ANTI * 'outer_path' is the cheapest outer path * 'inner_path' is the cheapest inner path @@ -4291,7 +4293,7 @@ MergePath* create_mergejoin_path(PlannerInfo* root, RelOptInfo* joinrel, JoinTyp * (this should be a subset of the restrict_clauses list) */ HashPath* create_hashjoin_path(PlannerInfo* root, RelOptInfo* joinrel, JoinType jointype, JoinCostWorkspace* workspace, - SpecialJoinInfo* sjinfo, SemiAntiJoinFactors* semifactors, Path* outer_path, Path* inner_path, + JoinPathExtraData *extra, Path* outer_path, Path* inner_path, List* restrict_clauses, Relids required_outer, List* hashclauses, int dop) { HashPath* pathnode = makeNode(HashPath); @@ -4300,7 +4302,7 @@ HashPath* create_hashjoin_path(PlannerInfo* root, RelOptInfo* joinrel, JoinType pathnode->jpath.path.pathtype = T_HashJoin; pathnode->jpath.path.parent = joinrel; pathnode->jpath.path.param_info = - get_joinrel_parampathinfo(root, joinrel, outer_path, inner_path, sjinfo, required_outer, &restrict_clauses); + get_joinrel_parampathinfo(root, joinrel, outer_path, inner_path, extra->sjinfo, required_outer, &restrict_clauses); /* * A hashjoin never has pathkeys, since its output ordering is @@ -4316,6 +4318,7 @@ HashPath* create_hashjoin_path(PlannerInfo* root, RelOptInfo* joinrel, JoinType pathnode->jpath.path.pathkeys = NIL; pathnode->jpath.path.dop = dop; pathnode->jpath.jointype = jointype; + pathnode->jpath.inner_unique = extra->inner_unique; pathnode->jpath.outerjoinpath = outer_path; pathnode->jpath.innerjoinpath = inner_path; pathnode->jpath.joinrestrictinfo = restrict_clauses; @@ -4338,8 +4341,7 @@ HashPath* create_hashjoin_path(PlannerInfo* root, RelOptInfo* joinrel, JoinType final_cost_hashjoin(root, pathnode, workspace, - sjinfo, - semifactors, + extra, check_join_method_alternative( restrict_clauses, outer_path->parent, inner_path->parent, jointype, &try_eq_related_indirectly), dop); @@ -6309,6 +6311,12 @@ static JoinPath* add_join_redistribute_path(PlannerInfo* root, RelOptInfo* joinr List* stream_distribute_key_outer, bool replicate_inner, bool replicate_outer, NodeTag nodetag, Distribution* target_distribution, List* inner_pathkeys = NIL, List* outer_pathkeys = NIL) { + JoinPathExtraData extra; + extra.inner_unique = false; + extra.sjinfo = sjinfo; + extra.semifactors = {0,0}; + if (semifactors)extra.semifactors = *semifactors; + Path* stream_path_inner = inner_path; Path* stream_path_outer = outer_path; JoinPath* joinpath = NULL; @@ -6388,14 +6396,13 @@ static JoinPath* add_join_redistribute_path(PlannerInfo* root, RelOptInfo* joinr /* Create join path. */ if (nodetag == T_HashJoin) { initial_cost_hashjoin( - root, workspace, jointype, hashclauses, stream_path_outer, stream_path_inner, sjinfo, semifactors, joinDop); + root, workspace, jointype, hashclauses, stream_path_outer, stream_path_inner, &extra, joinDop); joinpath = (JoinPath*)create_hashjoin_path(root, joinrel, jointype, workspace, - sjinfo, - semifactors, + &extra, stream_path_outer, stream_path_inner, restrictlist, @@ -6404,7 +6411,7 @@ static JoinPath* add_join_redistribute_path(PlannerInfo* root, RelOptInfo* joinr joinDop); } else { initial_cost_nestloop( - root, workspace, jointype, stream_path_outer, stream_path_inner, sjinfo, semifactors, joinDop); + root, workspace, jointype, stream_path_outer, stream_path_inner, &extra, joinDop); /* * When nestloop, hashclauses refer to pathkeys. @@ -6418,8 +6425,7 @@ static JoinPath* add_join_redistribute_path(PlannerInfo* root, RelOptInfo* joinr joinrel, jointype, workspace, - sjinfo, - semifactors, + &extra, stream_path_outer, stream_path_inner, restrictlist, @@ -6448,6 +6454,11 @@ static void add_hashjoin_broadcast_path(PlannerInfo* root, RelOptInfo* joinrel, bool is_replicate, bool stream_outer, Distribution* target_distribution, ParallelDesc* need_smpDesc, ParallelDesc* non_smpDesc, int dop) { + JoinPathExtraData extra; + extra.inner_unique = false; + extra.sjinfo = sjinfo; + extra.semifactors = *semifactors; + Path* streamed_path = NULL; Path* other_side = NULL; JoinPath* joinpath = NULL; @@ -6504,14 +6515,13 @@ static void add_hashjoin_broadcast_path(PlannerInfo* root, RelOptInfo* joinrel, new_inner_path = stream_outer ? other_side : streamed_path; initial_cost_hashjoin( - root, workspace, jointype, hashclauses, new_outer_path, new_inner_path, sjinfo, semifactors, dop); + root, workspace, jointype, hashclauses, new_outer_path, new_inner_path, &extra, dop); joinpath = (JoinPath*)create_hashjoin_path(root, joinrel, jointype, workspace, - sjinfo, - semifactors, + &extra, new_outer_path, new_inner_path, restrictlist, @@ -6589,6 +6599,7 @@ static List* add_join_parallel_path(StreamType inner_stream, StreamType outer_st List* stream_distribute_key_outer, List* hashclauses, List* restrictlist, NodeTag nodetag, Distribution* target_distribution, List* inner_pathkeys = NIL, List* outer_pathkeys = NIL) { + /* * When the user turn on SMP, we need to add parallel path * to the alternative path list. @@ -7099,6 +7110,12 @@ void add_hashjoin_path(PlannerInfo* root, RelOptInfo* joinrel, JoinType jointype JoinCostWorkspace* workspace, SpecialJoinInfo* sjinfo, SemiAntiJoinFactors* semifactors, Path* outer_path, Path* inner_path, List* restrictlist, Relids required_outer, List* hashclauses, Distribution* target_distribution) { + JoinPathExtraData extra; + extra.inner_unique = false; + extra.sjinfo = sjinfo; + extra.semifactors = {0,0}; + if (semifactors)extra.semifactors = *semifactors; + bool redistribute_inner = true; bool redistribute_outer = true; bool replicate_inner = false; @@ -7171,15 +7188,14 @@ void add_hashjoin_path(PlannerInfo* root, RelOptInfo* joinrel, JoinType jointype if (!parallel_enable(inner_path_t, outer_path_t)) { if (outer_path != outer_path_t || inner_path != inner_path_t) { initial_cost_hashjoin( - root, workspace, jointype, hashclauses, outer_path_t, inner_path_t, sjinfo, semifactors, 1); + root, workspace, jointype, hashclauses, outer_path_t, inner_path_t, &extra, 1); } joinpath = (JoinPath*)create_hashjoin_path(root, joinrel, jointype, workspace, - sjinfo, - semifactors, + &extra, outer_path_t, inner_path_t, restrictlist, @@ -7258,14 +7274,13 @@ void add_hashjoin_path(PlannerInfo* root, RelOptInfo* joinrel, JoinType jointype skew_stream, target_distribution); initial_cost_hashjoin( - root, workspace, jointype, hashclauses, outer_path, stream_path_inner, sjinfo, semifactors, 1); + root, workspace, jointype, hashclauses, outer_path, stream_path_inner, &extra, 1); joinpath = (JoinPath*)create_hashjoin_path(root, joinrel, jointype, workspace, - sjinfo, - semifactors, + &extra, outer_path, (Path*)stream_path_inner, restrictlist, @@ -7408,14 +7423,13 @@ void add_hashjoin_path(PlannerInfo* root, RelOptInfo* joinrel, JoinType jointype target_distribution); initial_cost_hashjoin( - root, workspace, jointype, hashclauses, stream_path_outer, inner_path, sjinfo, semifactors, 1); + root, workspace, jointype, hashclauses, stream_path_outer, inner_path, &extra, 1); joinpath = (JoinPath*)create_hashjoin_path(root, joinrel, jointype, workspace, - sjinfo, - semifactors, + &extra, (Path*)stream_path_outer, inner_path, restrictlist, @@ -7619,16 +7633,14 @@ void add_hashjoin_path(PlannerInfo* root, RelOptInfo* joinrel, JoinType jointype hashclauses, stream_path_outer, stream_path_inner, - sjinfo, - semifactors, + &extra, 1); joinpath = (JoinPath*)create_hashjoin_path(root, joinrel, jointype, workspace, - sjinfo, - semifactors, + &extra, (Path*)stream_path_outer, (Path*)stream_path_inner, restrictlist, @@ -7788,16 +7800,14 @@ void add_hashjoin_path(PlannerInfo* root, RelOptInfo* joinrel, JoinType jointype hashclauses, outer_path, stream_path_inner, - sjinfo, - semifactors, + &extra, 1); joinpath = (JoinPath*)create_hashjoin_path(root, joinrel, jointype, workspace, - sjinfo, - semifactors, + &extra, outer_path, (Path*)stream_path_inner, restrictlist, @@ -7859,16 +7869,14 @@ void add_hashjoin_path(PlannerInfo* root, RelOptInfo* joinrel, JoinType jointype hashclauses, stream_path_outer, inner_path, - sjinfo, - semifactors, + &extra, 1); joinpath = (JoinPath*)create_hashjoin_path(root, joinrel, jointype, workspace, - sjinfo, - semifactors, + &extra, (Path*)stream_path_outer, inner_path, restrictlist, @@ -7997,8 +8005,7 @@ void add_hashjoin_path(PlannerInfo* root, RelOptInfo* joinrel, JoinType jointype joinrel, jointype, workspace, - sjinfo, - semifactors, + &extra, outer_path, inner_path, restrictlist, @@ -8054,6 +8061,12 @@ static void add_nestloop_broadcast_path(PlannerInfo* root, RelOptInfo* joinrel, List* stream_pathkeys, bool is_replicate, bool stream_outer, Distribution* target_distribution, ParallelDesc* need_smpDesc, ParallelDesc* non_smpDesc, int dop) { + JoinPathExtraData extra; + extra.inner_unique = false; + extra.sjinfo = sjinfo; + extra.semifactors = {0,0}; + if (semifactors)extra.semifactors = *semifactors; + Path* streamed_path = NULL; Path* other_side = NULL; JoinPath* joinpath = NULL; @@ -8105,14 +8118,13 @@ static void add_nestloop_broadcast_path(PlannerInfo* root, RelOptInfo* joinrel, new_outer_path = stream_outer ? streamed_path : other_side; new_inner_path = stream_outer ? other_side : streamed_path; - initial_cost_nestloop(root, workspace, jointype, new_outer_path, new_inner_path, sjinfo, semifactors, dop); + initial_cost_nestloop(root, workspace, jointype, new_outer_path, new_inner_path, &extra, dop); joinpath = (JoinPath*)create_nestloop_path(root, joinrel, jointype, workspace, - sjinfo, - semifactors, + &extra, new_outer_path, new_inner_path, restrict_clauses, @@ -8139,6 +8151,12 @@ void add_nestloop_path(PlannerInfo* root, RelOptInfo* joinrel, JoinType jointype /* Full-outer join doesn't support nestloop yet */ AssertEreport(jointype != JOIN_FULL, MOD_OPT_JOIN, "Join type shouldn't be full join for nestloop"); + JoinPathExtraData extra; + extra.inner_unique = false; + extra.sjinfo = sjinfo; + extra.semifactors = {0,0}; + if (semifactors)extra.semifactors = *semifactors; + bool redistribute_inner = false; bool redistribute_outer = false; bool replicate_inner = false; @@ -8204,15 +8222,14 @@ void add_nestloop_path(PlannerInfo* root, RelOptInfo* joinrel, JoinType jointype if (!parallel_enable(inner_path_t, outer_path_t)) { if (outer_path != outer_path_t || inner_path != inner_path_t) { initial_cost_nestloop( - root, workspace, jointype, outer_path_t, inner_path_t, sjinfo, semifactors, 1); + root, workspace, jointype, outer_path_t, inner_path_t, &extra, 1); } joinpath = (JoinPath*)create_nestloop_path(root, joinrel, jointype, workspace, - sjinfo, - semifactors, + &extra, outer_path_t, inner_path_t, restrict_clauses, @@ -8304,14 +8321,13 @@ void add_nestloop_path(PlannerInfo* root, RelOptInfo* joinrel, JoinType jointype target_distribution); initial_cost_nestloop( - root, workspace, jointype, outer_path, stream_path_inner, sjinfo, semifactors, 1); + root, workspace, jointype, outer_path, stream_path_inner, &extra, 1); joinpath = (JoinPath*)create_nestloop_path(root, joinrel, jointype, workspace, - sjinfo, - semifactors, + &extra, outer_path, (Path*)stream_path_inner, restrict_clauses, @@ -8464,14 +8480,13 @@ void add_nestloop_path(PlannerInfo* root, RelOptInfo* joinrel, JoinType jointype target_distribution); initial_cost_nestloop( - root, workspace, jointype, stream_path_outer, inner_path, sjinfo, semifactors, 1); + root, workspace, jointype, stream_path_outer, inner_path, &extra, 1); joinpath = (JoinPath*)create_nestloop_path(root, joinrel, jointype, workspace, - sjinfo, - semifactors, + &extra, (Path*)stream_path_outer, inner_path, restrict_clauses, @@ -8678,14 +8693,13 @@ void add_nestloop_path(PlannerInfo* root, RelOptInfo* joinrel, JoinType jointype target_distribution); initial_cost_nestloop( - root, workspace, jointype, stream_path_outer, stream_path_inner, sjinfo, semifactors, 1); + root, workspace, jointype, stream_path_outer, stream_path_inner, &extra, 1); joinpath = (JoinPath*)create_nestloop_path(root, joinrel, jointype, workspace, - sjinfo, - semifactors, + &extra, (Path*)stream_path_outer, (Path*)stream_path_inner, restrict_clauses, @@ -8849,14 +8863,13 @@ void add_nestloop_path(PlannerInfo* root, RelOptInfo* joinrel, JoinType jointype target_distribution); initial_cost_nestloop( - root, workspace, jointype, outer_path, stream_path_inner, sjinfo, semifactors, 1); + root, workspace, jointype, outer_path, stream_path_inner, &extra, 1); joinpath = (JoinPath*)create_nestloop_path(root, joinrel, jointype, workspace, - sjinfo, - semifactors, + &extra, outer_path, (Path*)stream_path_inner, restrict_clauses, @@ -8915,14 +8928,13 @@ void add_nestloop_path(PlannerInfo* root, RelOptInfo* joinrel, JoinType jointype target_distribution); initial_cost_nestloop( - root, workspace, jointype, stream_path_outer, inner_path, sjinfo, semifactors, 1); + root, workspace, jointype, stream_path_outer, inner_path, &extra, 1); joinpath = (JoinPath*)create_nestloop_path(root, joinrel, jointype, workspace, - sjinfo, - semifactors, + &extra, (Path*)stream_path_outer, inner_path, restrict_clauses, @@ -9058,8 +9070,7 @@ void add_nestloop_path(PlannerInfo* root, RelOptInfo* joinrel, JoinType jointype joinrel, jointype, workspace, - sjinfo, - semifactors, + &extra, outer_path, inner_path, restrict_clauses, @@ -9106,6 +9117,11 @@ static void add_mergejoin_broadcast_path(PlannerInfo* root, RelOptInfo* joinrel, List* outersortkeys, List* innersortkeys, List* stream_pathkeys, List* non_stream_pathkeys, bool is_replicate, bool stream_outer, Distribution* target_distribution) { + JoinPathExtraData extra; + extra.inner_unique = false; + extra.sjinfo = sjinfo; + extra.semifactors = {0,0}; + Path* streamed_path = NULL; JoinPath* joinpath = NULL; Path* new_outer_path = NULL; @@ -9139,13 +9155,13 @@ static void add_mergejoin_broadcast_path(PlannerInfo* root, RelOptInfo* joinrel, new_inner_path = stream_outer ? non_stream_path : streamed_path; initial_cost_mergejoin( - root, workspace, jointype, mergeclauses, new_outer_path, new_inner_path, outersortkeys, innersortkeys, sjinfo); + root, workspace, jointype, mergeclauses, new_outer_path, new_inner_path, outersortkeys, innersortkeys, &extra); joinpath = (JoinPath*)create_mergejoin_path(root, joinrel, jointype, workspace, - sjinfo, + &extra, new_outer_path, new_inner_path, restrict_clauses, @@ -9178,6 +9194,11 @@ void add_mergejoin_path(PlannerInfo* root, RelOptInfo* joinrel, JoinType jointyp List* rrinfo_outer = NULL; List* stream_distribute_key = NIL; + JoinPathExtraData extra; + extra.inner_unique = false; + extra.sjinfo = sjinfo; + extra.semifactors = {0,0}; + /* Only create unparallel path for mergejoin. */ if (inner_path->dop > 1 || outer_path->dop > 1) return; @@ -9235,14 +9256,14 @@ void add_mergejoin_path(PlannerInfo* root, RelOptInfo* joinrel, JoinType jointyp inner_path_t, outersortkeys, innersortkeys, - sjinfo); + &extra); } joinpath = (JoinPath*)create_mergejoin_path(root, joinrel, jointype, workspace, - sjinfo, + &extra, outer_path_t, inner_path_t, restrict_clauses, @@ -9303,13 +9324,13 @@ void add_mergejoin_path(PlannerInfo* root, RelOptInfo* joinrel, JoinType jointyp stream_path_inner, outersortkeys, innersortkeys, - sjinfo); + &extra); joinpath = (JoinPath*)create_mergejoin_path(root, joinrel, jointype, workspace, - sjinfo, + &extra, outer_path, (Path*)stream_path_inner, restrict_clauses, @@ -9402,13 +9423,13 @@ void add_mergejoin_path(PlannerInfo* root, RelOptInfo* joinrel, JoinType jointyp inner_path, outersortkeys, innersortkeys, - sjinfo); + &extra); joinpath = (JoinPath*)create_mergejoin_path(root, joinrel, jointype, workspace, - sjinfo, + &extra, (Path*)stream_path_outer, inner_path, restrict_clauses, @@ -9554,13 +9575,13 @@ void add_mergejoin_path(PlannerInfo* root, RelOptInfo* joinrel, JoinType jointyp stream_path_inner, outersortkeys, innersortkeys, - sjinfo); + &extra); joinpath = (JoinPath*)create_mergejoin_path(root, joinrel, jointype, workspace, - sjinfo, + &extra, (Path*)stream_path_outer, (Path*)stream_path_inner, restrict_clauses, @@ -9657,13 +9678,13 @@ void add_mergejoin_path(PlannerInfo* root, RelOptInfo* joinrel, JoinType jointyp stream_path_inner, outersortkeys, innersortkeys, - sjinfo); + &extra); joinpath = (JoinPath*)create_mergejoin_path(root, joinrel, jointype, workspace, - sjinfo, + &extra, outer_path, (Path*)stream_path_inner, restrict_clauses, @@ -9709,13 +9730,13 @@ void add_mergejoin_path(PlannerInfo* root, RelOptInfo* joinrel, JoinType jointyp inner_path, outersortkeys, innersortkeys, - sjinfo); + &extra); joinpath = (JoinPath*)create_mergejoin_path(root, joinrel, jointype, workspace, - sjinfo, + &extra, (Path*)stream_path_outer, inner_path, restrict_clauses, @@ -9786,7 +9807,7 @@ void add_mergejoin_path(PlannerInfo* root, RelOptInfo* joinrel, JoinType jointyp joinrel, jointype, workspace, - sjinfo, + &extra, outer_path, inner_path, restrict_clauses, diff --git a/src/gausskernel/optimizer/util/relnode.cpp b/src/gausskernel/optimizer/util/relnode.cpp index 23bd0919a..95d6ffc5e 100755 --- a/src/gausskernel/optimizer/util/relnode.cpp +++ b/src/gausskernel/optimizer/util/relnode.cpp @@ -206,6 +206,8 @@ RelOptInfo* build_simple_rel(PlannerInfo* root, int relid, RelOptKind reloptkind rel->useridiscurrent = false; rel->fdwroutine = NULL; rel->fdw_private = NULL; + rel->unique_for_rels = NIL; + rel->non_unique_for_rels = NIL; rel->baserestrictinfo = NIL; rel->baserestrictcost.startup = 0; rel->baserestrictcost.per_tuple = 0; @@ -666,6 +668,8 @@ RelOptInfo* build_join_rel(PlannerInfo* root, Relids joinrelids, RelOptInfo* out joinrel->subplan_params = NIL; joinrel->fdwroutine = NULL; joinrel->fdw_private = NULL; + joinrel->unique_for_rels = NIL; + joinrel->non_unique_for_rels = NIL; joinrel->baserestrictinfo = NIL; joinrel->baserestrictcost.startup = 0; joinrel->baserestrictcost.per_tuple = 0; diff --git a/src/gausskernel/runtime/executor/nodeHashjoin.cpp b/src/gausskernel/runtime/executor/nodeHashjoin.cpp index eb9a01deb..28e3a9b40 100755 --- a/src/gausskernel/runtime/executor/nodeHashjoin.cpp +++ b/src/gausskernel/runtime/executor/nodeHashjoin.cpp @@ -351,10 +351,9 @@ static TupleTableSlot* ExecHashJoin(PlanState* state) continue; } - /* Semi join: we'll consider returning the first match, but after - * that we're done with this outer tuple */ - if (jointype == JOIN_SEMI) + if (node->js.single_match) { node->hj_JoinState = HJ_NEED_NEW_OUTER; + } } if (otherqual == NIL || ExecQual(otherqual, econtext, false)) { @@ -577,6 +576,8 @@ HashJoinState* ExecInitHashJoin(HashJoin* node, EState* estate, int eflags) ExecInitResultTupleSlot(estate, &hjstate->js.ps); hjstate->hj_OuterTupleSlot = ExecInitExtraTupleSlot(estate); + hjstate->js.single_match = (node->join.inner_unique || node->join.jointype == JOIN_SEMI); + /* set up null tuples for outer joins, if needed */ switch (node->join.jointype) { case JOIN_INNER: diff --git a/src/gausskernel/runtime/executor/nodeMergejoin.cpp b/src/gausskernel/runtime/executor/nodeMergejoin.cpp index e5baa3efe..11be89b57 100644 --- a/src/gausskernel/runtime/executor/nodeMergejoin.cpp +++ b/src/gausskernel/runtime/executor/nodeMergejoin.cpp @@ -786,12 +786,9 @@ static TupleTableSlot* ExecMergeJoin(PlanState* state) break; } - /* - * In a semijoin, we'll consider returning the first - * match, but after that we're done with this outer tuple. - */ - if (node->js.jointype == JOIN_SEMI) + if (node->js.single_match) { node->mj_JoinState = EXEC_MJ_NEXTOUTER; + } qual_result = (other_qual == NIL || ExecQual(other_qual, econtext, false)); MJ_DEBUG_QUAL(other_qual, qual_result); @@ -1043,16 +1040,11 @@ static TupleTableSlot* ExecMergeJoin(PlanState* state) * forcing the merge clause to never match, so we never * get here. */ - ExecRestrPos(inner_plan); + if (!node->mj_SkipMarkRestore) { + ExecRestrPos(inner_plan); + node->mj_InnerTupleSlot = inner_tuple_slot; + } - /* - * ExecRestrPos probably should give us back a new Slot, - * but since it doesn't, use the marked slot. (The - * previously returned mj_InnerTupleSlot cannot be assumed - * to hold the required tuple.) - */ - node->mj_InnerTupleSlot = inner_tuple_slot; - /* we need not do MJEvalInnerValues again */ node->mj_JoinState = EXEC_MJ_JOINTUPLES; } else { /* ---------------- @@ -1149,7 +1141,10 @@ static TupleTableSlot* ExecMergeJoin(PlanState* state) MJ_DEBUG_COMPARE(compare_result); if (compare_result == 0) { - ExecMarkPos(inner_plan); + if (!node->mj_SkipMarkRestore) { + ExecMarkPos(inner_plan); + } + if (node->mj_InnerTupleSlot == NULL) { ereport(ERROR, (errcode(ERRCODE_UNEXPECTED_NULL_VALUE), @@ -1437,29 +1432,19 @@ MergeJoinState* ExecInitMergeJoin(MergeJoin* node, EState* estate, int eflags) merge_state->js.joinqual = (List*)ExecInitExpr((Expr*)node->join.joinqual, (PlanState*)merge_state); merge_state->js.nulleqqual = (List*)ExecInitExpr((Expr*)node->join.nulleqqual, (PlanState*)merge_state); merge_state->mj_ConstFalseJoin = false; - /* merge_clauses are handled below */ - /* - * initialize child nodes - * - * inner child must support MARK/RESTORE. - */ - outerPlanState(merge_state) = ExecInitNode(outerPlan(node), estate, eflags); - innerPlanState(merge_state) = ExecInitNode(innerPlan(node), estate, eflags | EXEC_FLAG_MARK); - /* - * For certain types of inner child nodes, it is advantageous to issue - * MARK every time we advance past an inner tuple we will never return to. - * For other types, MARK on a tuple we cannot return to is a waste of - * cycles. Detect which case applies and set mj_ExtraMarks if we want to - * issue "unnecessary" MARK calls. - * - * Currently, only Material wants the extra MARKs, and it will be helpful - * only if eflags doesn't specify REWIND. - */ - if (IsA(innerPlan(node), Material) && (eflags & EXEC_FLAG_REWIND) == 0) + Assert(node->join.joinqual == NIL || !node->skip_mark_restore); + merge_state->mj_SkipMarkRestore = node->skip_mark_restore; + + outerPlanState(merge_state) = ExecInitNode(outerPlan(node), estate, eflags); + innerPlanState(merge_state) = + ExecInitNode(innerPlan(node), estate, merge_state->mj_SkipMarkRestore ? eflags : (eflags | EXEC_FLAG_MARK)); + + if (IsA(innerPlan(node), Material) && (eflags & EXEC_FLAG_REWIND) == 0 && !merge_state->mj_SkipMarkRestore) { merge_state->mj_ExtraMarks = true; - else + } else { merge_state->mj_ExtraMarks = false; + } /* * tuple table initialization @@ -1469,6 +1454,8 @@ MergeJoinState* ExecInitMergeJoin(MergeJoin* node, EState* estate, int eflags) merge_state->mj_MarkedTupleSlot = ExecInitExtraTupleSlot(estate); ExecSetSlotDescriptor(merge_state->mj_MarkedTupleSlot, ExecGetResultType(innerPlanState(merge_state))); + merge_state->js.single_match = (node->join.inner_unique || node->join.jointype == JOIN_SEMI); + switch (node->join.jointype) { case JOIN_INNER: case JOIN_SEMI: diff --git a/src/gausskernel/runtime/executor/nodeNestloop.cpp b/src/gausskernel/runtime/executor/nodeNestloop.cpp index 0bfab53b3..70365d860 100644 --- a/src/gausskernel/runtime/executor/nodeNestloop.cpp +++ b/src/gausskernel/runtime/executor/nodeNestloop.cpp @@ -282,12 +282,9 @@ static TupleTableSlot* ExecNestLoop(PlanState* state) continue; /* return to top of loop */ } - /* - * In a semijoin, we'll consider returning the first match, but - * after that we're done with this outer tuple. - */ - if (node->js.jointype == JOIN_SEMI) + if (node->js.single_match) { node->nl_NeedNewOuter = true; + } if (otherqual == NIL || ExecQual(otherqual, econtext, false)) { /* @@ -383,6 +380,8 @@ NestLoopState* ExecInitNestLoop(NestLoop* node, EState* estate, int eflags) */ ExecInitResultTupleSlot(estate, &nlstate->js.ps); + nlstate->js.single_match = (node->join.inner_unique || node->join.jointype == JOIN_SEMI); + switch (node->join.jointype) { case JOIN_INNER: case JOIN_SEMI: diff --git a/src/include/knl/knl_guc/knl_session_attr_sql.h b/src/include/knl/knl_guc/knl_session_attr_sql.h index c1ee290ca..f4b3af22d 100644 --- a/src/include/knl/knl_guc/knl_session_attr_sql.h +++ b/src/include/knl/knl_guc/knl_session_attr_sql.h @@ -117,6 +117,7 @@ typedef struct knl_session_attr_sql { #ifdef DEBUG_BOUNDED_SORT bool optimize_bounded_sort; #endif + bool enable_inner_unique_opt; bool escape_string_warning; bool standard_conforming_strings; bool enable_light_proxy; diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h index 9af57da8b..2d7be305f 100644 --- a/src/include/miscadmin.h +++ b/src/include/miscadmin.h @@ -38,6 +38,7 @@ * Backend version and inplace upgrade staffs *****************************************************************************/ +extern const uint32 INNER_UNIQUE_VERSION_NUM; extern const uint32 PARTITION_ENHANCE_VERSION_NUM; extern const uint32 SELECT_INTO_FILE_VERSION_NUM; extern const uint32 CHARACTER_SET_VERSION_NUM; diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index 921ef2617..92e9034eb 100755 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -2097,6 +2097,7 @@ typedef struct ExtensiblePlanState { typedef struct JoinState { PlanState ps; JoinType jointype; + bool single_match; List* joinqual; /* JOIN quals (in addition to ps.qual) */ List* nulleqqual; } JoinState; @@ -2123,6 +2124,7 @@ typedef struct NestLoopState { * NumClauses number of mergejoinable join clauses * Clauses info for each mergejoinable clause * JoinState current state of ExecMergeJoin state machine + * SkipMarkRestore true if we may skip Mark and Restore operations * ExtraMarks true to issue extra Mark operations on inner scan * ConstFalseJoin true if we have a constant-false joinqual * FillOuter true if should emit unjoined outer tuples anyway @@ -2146,6 +2148,7 @@ typedef struct MergeJoinState { int mj_NumClauses; MergeJoinClause mj_Clauses; /* array of length mj_NumClauses */ int mj_JoinState; + bool mj_SkipMarkRestore; bool mj_ExtraMarks; bool mj_ConstFalseJoin; bool mj_FillOuter; diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h index 1469f8c19..436d2f19e 100644 --- a/src/include/nodes/plannodes.h +++ b/src/include/nodes/plannodes.h @@ -1015,11 +1015,13 @@ typedef struct ExtensiblePlan { * (But plan.qual is still applied before actually returning a tuple.) * For an outer join, only joinquals are allowed to be used as the merge * or hash condition of a merge or hash join. + * * ---------------- */ typedef struct Join { Plan plan; JoinType jointype; + bool inner_unique; List* joinqual; /* JOIN quals (in addition to plan.qual) */ /* * @hdfs @@ -1071,6 +1073,7 @@ typedef struct NestLoopParam { */ typedef struct MergeJoin { Join join; + bool skip_mark_restore; /* Can we skip mark/restore calls? */ List* mergeclauses; /* mergeclauses as expression trees */ /* these are arrays, but have the same length as the mergeclauses list: */ Oid* mergeFamilies; /* per-clause OIDs of btree opfamilies */ diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h index 2ad126770..1a6fcd3d9 100755 --- a/src/include/nodes/relation.h +++ b/src/include/nodes/relation.h @@ -570,7 +570,7 @@ typedef struct PlannerInfo { * useridiscurrent - we've assumed that userid equals current user * fdwroutine - function hooks for FDW, if foreign table (else NULL) * fdw_private - private state for FDW, if foreign table (else NULL) - * + * * The presence of the remaining fields depends on the restrictions * and joins that the relation participates in: * @@ -694,6 +694,10 @@ typedef struct RelOptInfo { struct FdwRoutine* fdwroutine; /* if foreign table */ void* fdw_private; /* if foreign table */ + /* cache space for remembering if we have proven this relation unique */ + List *unique_for_rels; /* known unique for these other relid set(s) */ + List *non_unique_for_rels; /* known not unique for these set(s) */ + /* used by various scans and joins: */ List* baserestrictinfo; /* RestrictInfo structures (if base * rel) */ @@ -1320,6 +1324,8 @@ typedef struct JoinPath { Path path; JoinType jointype; + bool inner_unique; /* each outer tuple provably matches no more + * than one inner tuple */ Path* outerjoinpath; /* path for the outer side of the join */ Path* innerjoinpath; /* path for the inner side of the join */ @@ -1371,6 +1377,7 @@ typedef struct MergePath { List* path_mergeclauses; /* join clauses to be used for merge */ List* outersortkeys; /* keys for explicit sort, if any */ List* innersortkeys; /* keys for explicit sort, if any */ + bool skip_mark_restore; /* can executor skip mark/restore? */ bool materialize_inner; /* add Materialize to inner? */ OpMemInfo outer_mem_info; /* Mem info for outer explicit sort */ OpMemInfo inner_mem_info; /* Mem info for inner explicit sort */ @@ -1994,8 +2001,8 @@ typedef struct PlannerParamItem { } PlannerParamItem; /* - * When making cost estimates for a SEMI or ANTI join, there are some - * correction factors that are needed in both nestloop and hash joins + * When making cost estimates for a SEMI/ANTI/inner_unique join, there are + * some correction factors that are needed in both nestloop and hash joins * to account for the fact that the executor can stop scanning inner rows * as soon as it finds a match to the current outer row. These numbers * depend only on the selected outer and inner join relations, not on the @@ -2017,6 +2024,13 @@ typedef struct SemiAntiJoinFactors { Selectivity match_count; } SemiAntiJoinFactors; +typedef struct JoinPathExtraData +{ + bool inner_unique; + SpecialJoinInfo *sjinfo; + SemiAntiJoinFactors semifactors; +} JoinPathExtraData; + /* * For speed reasons, cost estimation for join paths is performed in two * phases: the first phase tries to quickly derive a lower bound for the diff --git a/src/include/optimizer/cost.h b/src/include/optimizer/cost.h index 81d1cd473..f13d9f248 100644 --- a/src/include/optimizer/cost.h +++ b/src/include/optimizer/cost.h @@ -120,18 +120,18 @@ extern void cost_group(Path* path, PlannerInfo* root, int numGroupCols, double n Cost input_total_cost, double input_tuples); extern void cost_limit(Plan* plan, Plan* lefttree, int64 offset_est, int64 count_est); extern void initial_cost_nestloop(PlannerInfo* root, JoinCostWorkspace* workspace, JoinType jointype, Path* outer_path, - Path* inner_path, SpecialJoinInfo* sjinfo, SemiAntiJoinFactors* semifactors, int dop); + Path* inner_path, JoinPathExtraData *extra, int dop); extern void final_cost_nestloop(PlannerInfo* root, NestPath* path, JoinCostWorkspace* workspace, - SpecialJoinInfo* sjinfo, SemiAntiJoinFactors* semifactors, bool hasalternative, int dop); + JoinPathExtraData *extra, bool hasalternative, int dop); extern void initial_cost_mergejoin(PlannerInfo* root, JoinCostWorkspace* workspace, JoinType jointype, List* mergeclauses, Path* outer_path, Path* inner_path, List* outersortkeys, List* innersortkeys, - SpecialJoinInfo* sjinfo); + JoinPathExtraData *extra); extern void final_cost_mergejoin( - PlannerInfo* root, MergePath* path, JoinCostWorkspace* workspace, SpecialJoinInfo* sjinfo, bool hasalternative); + PlannerInfo* root, MergePath* path, JoinCostWorkspace* workspace, JoinPathExtraData *extra, bool hasalternative); extern void initial_cost_hashjoin(PlannerInfo* root, JoinCostWorkspace* workspace, JoinType jointype, List* hashclauses, - Path* outer_path, Path* inner_path, SpecialJoinInfo* sjinfo, SemiAntiJoinFactors* semifactors, int dop); + Path* outer_path, Path* inner_path, JoinPathExtraData *extra, int dop); extern void final_cost_hashjoin(PlannerInfo* root, HashPath* path, JoinCostWorkspace* workspace, - SpecialJoinInfo* sjinfo, SemiAntiJoinFactors* semifactors, bool hasalternative, int dop); + JoinPathExtraData *extra, bool hasalternative, int dop); extern void cost_rescan(PlannerInfo* root, Path* path, Cost* rescan_startup_cost, /* output parameters */ Cost* rescan_total_cost, OpMemInfo* mem_info); extern Cost cost_rescan_material(double rows, int width, OpMemInfo* mem_info, bool vectorized, int dop); diff --git a/src/include/optimizer/pathnode.h b/src/include/optimizer/pathnode.h index 77bd4faa6..f2950e64b 100644 --- a/src/include/optimizer/pathnode.h +++ b/src/include/optimizer/pathnode.h @@ -89,15 +89,15 @@ extern Relids calc_nestloop_required_outer(Path* outer_path, Path* inner_path); extern Relids calc_non_nestloop_required_outer(Path* outer_path, Path* inner_path); extern NestPath* create_nestloop_path(PlannerInfo* root, RelOptInfo* joinrel, JoinType jointype, - JoinCostWorkspace* workspace, SpecialJoinInfo* sjinfo, SemiAntiJoinFactors* semifactors, Path* outer_path, + JoinCostWorkspace* workspace, JoinPathExtraData* extra, Path* outer_path, Path* inner_path, List* restrict_clauses, List* pathkeys, Relids required_outer, int dop = 1); extern MergePath* create_mergejoin_path(PlannerInfo* root, RelOptInfo* joinrel, JoinType jointype, - JoinCostWorkspace* workspace, SpecialJoinInfo* sjinfo, Path* outer_path, Path* inner_path, List* restrict_clauses, + JoinCostWorkspace* workspace, JoinPathExtraData* extra, Path* outer_path, Path* inner_path, List* restrict_clauses, List* pathkeys, Relids required_outer, List* mergeclauses, List* outersortkeys, List* innersortkeys); extern HashPath* create_hashjoin_path(PlannerInfo* root, RelOptInfo* joinrel, JoinType jointype, - JoinCostWorkspace* workspace, SpecialJoinInfo* sjinfo, SemiAntiJoinFactors* semifactors, Path* outer_path, + JoinCostWorkspace* workspace, JoinPathExtraData* extra, Path* outer_path, Path* inner_path, List* restrict_clauses, Relids required_outer, List* hashclauses, int dop = 1); extern Path* reparameterize_path(PlannerInfo* root, Path* path, Relids required_outer, double loop_count); diff --git a/src/include/optimizer/planmain.h b/src/include/optimizer/planmain.h index a0bd5f42e..c72ebf94a 100755 --- a/src/include/optimizer/planmain.h +++ b/src/include/optimizer/planmain.h @@ -168,6 +168,8 @@ extern bool useInformationalConstraint(PlannerInfo* root, List* qualClause, Reli extern List* remove_useless_joins(PlannerInfo* root, List* joinlist); extern bool query_supports_distinctness(Query* query); extern bool query_is_distinct_for(Query* query, List* colnos, List* opids); +extern bool innerrel_is_unique(PlannerInfo *root, RelOptInfo *outerrel, RelOptInfo *innerrel, + JoinType jointype, List *restrictlist); /* * prototypes for plan/setrefs.c diff --git a/src/include/optimizer/streampath.h b/src/include/optimizer/streampath.h index 0a9366aee..6e21d24a3 100644 --- a/src/include/optimizer/streampath.h +++ b/src/include/optimizer/streampath.h @@ -49,7 +49,7 @@ protected: class JoinPathGenBase : public PathGen { public: JoinPathGenBase(PlannerInfo* root, RelOptInfo* joinrel, JoinType jointype, JoinType save_jointype, - SpecialJoinInfo* sjinfo, SemiAntiJoinFactors* semifactors, List* joinclauses, List* restrictinfo, + JoinPathExtraData* extra, List* joinclauses, List* restrictinfo, Path* outer_path, Path* inner_path, Relids required_outer); virtual ~JoinPathGenBase(); @@ -136,11 +136,8 @@ protected: /* Cost work space for join. */ JoinCostWorkspace* m_workspace; - /* Special join info. */ - SpecialJoinInfo* m_sjinfo; - - /* Join facctors for semi and anti join. */ - SemiAntiJoinFactors* m_semifactors; + /*extra data for join*/ + JoinPathExtraData* m_extra; /* Join clauses list. */ List* m_joinClauses; @@ -236,7 +233,7 @@ protected: class JoinPathGen : public JoinPathGenBase { public: JoinPathGen(PlannerInfo* root, RelOptInfo* joinrel, JoinType jointype, JoinType save_jointype, - SpecialJoinInfo* sjinfo, SemiAntiJoinFactors* semifactors, List* joinclauses, List* restrictinfo, + JoinPathExtraData* extra, List* joinclauses, List* restrictinfo, Path* outer_path, Path* inner_path, Relids required_outer); virtual ~JoinPathGen(); @@ -301,7 +298,7 @@ protected: class HashJoinPathGen : public JoinPathGen { public: HashJoinPathGen(PlannerInfo* root, RelOptInfo* joinrel, JoinType jointype, JoinType save_jointype, - SpecialJoinInfo* sjinfo, SemiAntiJoinFactors* semifactors, Path* outer_path, Path* inner_path, + JoinPathExtraData* extra, Path* outer_path, Path* inner_path, List* restrictlist, Relids required_outer, List* hashclauses); virtual ~HashJoinPathGen(); @@ -330,7 +327,7 @@ private: class NestLoopPathGen : public JoinPathGen { public: NestLoopPathGen(PlannerInfo* root, RelOptInfo* joinrel, JoinType jointype, JoinType save_jointype, - SpecialJoinInfo* sjinfo, SemiAntiJoinFactors* semifactors, Path* outer_path, Path* inner_path, + JoinPathExtraData* extra, Path* outer_path, Path* inner_path, List* restrictlist, List* pathkeys, Relids required_outer); virtual ~NestLoopPathGen(); @@ -355,7 +352,7 @@ private: class MergeJoinPathGen : public JoinPathGen { public: MergeJoinPathGen(PlannerInfo* root, RelOptInfo* joinrel, JoinType jointype, JoinType save_jointype, - SpecialJoinInfo* sjinfo, SemiAntiJoinFactors* semifactors, Path* outer_path, Path* inner_path, + JoinPathExtraData* extra, Path* outer_path, Path* inner_path, List* restrict_clauses, List* pathkeys, Relids required_outer, List* mergeclauses, List* outersortkeys, List* innersortkeys); diff --git a/src/test/regress/expected/hw_subpartition_scan.out b/src/test/regress/expected/hw_subpartition_scan.out index 85c6a7823..d4f451f23 100644 --- a/src/test/regress/expected/hw_subpartition_scan.out +++ b/src/test/regress/expected/hw_subpartition_scan.out @@ -662,6 +662,7 @@ explain (format yaml, costs off) select * from list_range_02 where col_2 in - Plan: + Node Type: "Nested Loop" + Join Type: "Semi" + + Inner Unique: false + Join Filter: "(subpartition_scan.list_range_02.col_2 = subpartition_scan.list_range_02.col_1)" + Plans: + - Node Type: "Partition Iterator" + @@ -706,6 +707,7 @@ explain (format json, costs off) select * from list_range_02 where col_2 in "Plan": { + "Node Type": "Nested Loop", + "Join Type": "Semi", + + "Inner Unique": false, + "Join Filter": "(subpartition_scan.list_range_02.col_2 = subpartition_scan.list_range_02.col_1)", + "Plans": [ + { + diff --git a/src/test/regress/expected/multi_delete.out b/src/test/regress/expected/multi_delete.out index 80b024efb..fada085d4 100644 --- a/src/test/regress/expected/multi_delete.out +++ b/src/test/regress/expected/multi_delete.out @@ -313,6 +313,7 @@ explain(format xml) delete from t_t_mutil_t1 a,t_t_mutil_t2 b where a.col2=b.col 355.67 + 23091 + 12 + + false + (a.col2 = b.col2) + + + @@ -375,6 +376,7 @@ explain(format json) delete from t_t_mutil_t1 a,t_t_mutil_t2 b where a.col2=b.co "Total Cost": 355.67, + "Plan Rows": 23091, + "Plan Width": 12, + + "Inner Unique": false, + "Hash Cond": "(a.col2 = b.col2)", + "Plans": [ + { + @@ -434,6 +436,7 @@ explain(format yaml) delete from t_t_mutil_t1 a,t_t_mutil_t2 b where a.col2=b.co Total Cost: 355.67 + Plan Rows: 23091 + Plan Width: 12 + + Inner Unique: false + Hash Cond: "(a.col2 = b.col2)" + Plans: + - Node Type: "Seq Scan" + diff --git a/src/test/regress/expected/multi_update.out b/src/test/regress/expected/multi_update.out index 984fe9e18..680918b97 100644 --- a/src/test/regress/expected/multi_update.out +++ b/src/test/regress/expected/multi_update.out @@ -347,6 +347,7 @@ explain(format xml) update public.t_t_mutil_t1 a,t_t_mutil_t2 b set b.col2=5,a.c 355.67 + 23091 + 20 + + false + (a.col1 = b.col1) + + + @@ -409,6 +410,7 @@ explain(format json) update public.t_t_mutil_t1 a,t_t_mutil_t2 b set b.col2=5,a. "Total Cost": 355.67, + "Plan Rows": 23091, + "Plan Width": 20, + + "Inner Unique": false, + "Hash Cond": "(a.col1 = b.col1)", + "Plans": [ + { + @@ -468,6 +470,7 @@ explain(format yaml) update public.t_t_mutil_t1 a,t_t_mutil_t2 b set b.col2=5,a. Total Cost: 355.67 + Plan Rows: 23091 + Plan Width: 20 + + Inner Unique: false + Hash Cond: "(a.col1 = b.col1)" + Plans: + - Node Type: "Seq Scan" + diff --git a/src/test/regress/output/recovery_2pc_tools.source b/src/test/regress/output/recovery_2pc_tools.source index bf737415b..d1c88d3e9 100644 --- a/src/test/regress/output/recovery_2pc_tools.source +++ b/src/test/regress/output/recovery_2pc_tools.source @@ -271,6 +271,7 @@ select name,vartype,unit,min_val,max_val from pg_settings where name <> 'qunit_c enable_indexonlyscan | bool | | | enable_indexscan | bool | | | enable_indexscan_optimization | bool | | | + enable_inner_unique_opt | bool | | | enable_instance_metric_persistent | bool | | | enable_instr_cpu_timer | bool | | | enable_instr_rt_percentile | bool | | |