diff --git a/src/common/backend/utils/misc/guc/guc_sql.cpp b/src/common/backend/utils/misc/guc/guc_sql.cpp index 12f870a369d78508830bb7f84b63895e438631ca..06f23a0404d38301b2a17b15a81282a62bfdbb8c 100755 --- a/src/common/backend/utils/misc/guc/guc_sql.cpp +++ b/src/common/backend/utils/misc/guc/guc_sql.cpp @@ -319,6 +319,7 @@ static const struct config_enum_entry sql_beta_options[] = { {"partition_fdw_on", PARTITION_FDW_ON, false}, {"disable_bitmap_cost_with_lossy_pages", DISABLE_BITMAP_COST_WITH_LOSSY_PAGES, false}, {"extract_pushdown_or_clause", EXTRACT_PUSHDOWN_OR_CLAUSE, false}, + {"sorted_index_path_opt", SORTED_INDEX_PATH_OPT, false}, {NULL, 0, false} }; diff --git a/src/gausskernel/optimizer/path/allpaths.cpp b/src/gausskernel/optimizer/path/allpaths.cpp index bf5ac06a2963987e1edf782d395047d1256cbad1..1c6a860ea5a479783851317547e17decde1f4eda 100755 --- a/src/gausskernel/optimizer/path/allpaths.cpp +++ b/src/gausskernel/optimizer/path/allpaths.cpp @@ -4254,6 +4254,36 @@ bool is_single_baseresult_plan(Plan* plan) return IsA(plan, BaseResult) ? (plan->lefttree == NULL) : false; } +/* is_indexpath_has_unkey_quals + * check all quals in index path is using index key + * !! true not all in keys , false all is in keys + */ +bool is_indexpath_has_unkey_quals(Node* node, void* context) +{ + IndexPathKeyInfo* kinfo = (IndexPathKeyInfo*)context; + Var* var = NULL; + bool matched = false; + /* original deal treate as all in keys */ + if (kinfo == NULL || kinfo->keys == NULL) + return false; + if (IsA(node, Var)) { + /* we do not check varno cause it must be right here */ + var = (Var*)node; + for (int i = 0; i < kinfo->ncols; ++i) { + if (kinfo->keys[i] == var->varattno) { + matched = true; + break; + } + } + if (matched) + return expression_tree_walker(node, (bool (*)())is_indexpath_has_unkey_quals, context); + else + return true; + } + /* let it go */ + return expression_tree_walker(node, (bool (*)())is_indexpath_has_unkey_quals, context); +} + /***************************************************************************** * DEBUG SUPPORT *****************************************************************************/ diff --git a/src/gausskernel/optimizer/plan/planmain.cpp b/src/gausskernel/optimizer/plan/planmain.cpp index 1410b7a7cddcb20ea58e910778067eaaa41b0b3e..ced39cd0ba3c709fbc4d40bebb23f37cc0e47298 100755 --- a/src/gausskernel/optimizer/plan/planmain.cpp +++ b/src/gausskernel/optimizer/plan/planmain.cpp @@ -624,6 +624,10 @@ void generate_cheapest_and_sorted_path(PlannerInfo* root, root->glob->vectorized); } + if (ENABLE_SQL_BETA_FEATURE(SORTED_INDEX_PATH_OPT)) { + change_sortedpath_fraction(sortedpath, &tuple_fraction, limit_tuples); + } + if (compare_fractional_path_costs(sortedpath, &sort_path, tuple_fraction) > 0) { /* Presorted path is a loser */ debug_print_log(root, sortedpath, DEBUG2); diff --git a/src/gausskernel/optimizer/util/pathnode.cpp b/src/gausskernel/optimizer/util/pathnode.cpp index 54e2891c5682bef6fe260f927d6481d1b19e0349..31ea011a7c07788d38bde95ff9bbb8a1e731ad35 100755 --- a/src/gausskernel/optimizer/util/pathnode.cpp +++ b/src/gausskernel/optimizer/util/pathnode.cpp @@ -10221,4 +10221,54 @@ bool judge_node_compatible(PlannerInfo* root, Node* n1, Node* n2) return true; } +/* + * input: sortedPath - an index path which representative a sorted path + * fraction - a pointer,it value will be changed in this function + * nlimit - a number that used in limit conditiion + */ +void change_sortedpath_fraction(Path* sortedPath, double* fraction, double limit_tuples) +{ + /* all tuples that the index have */ + double totalTups = 0; + /* selected tuples after filter */ + double nTups = 0; + /* we just let memory context to free this pointer */ + IndexPathKeyInfo* info = (IndexPathKeyInfo*)palloc0(sizeof(IndexPathKeyInfo)); + IndexPath* path = NULL; + /* we only handle index path situation */ + if (sortedPath == NULL || !IsA(sortedPath, IndexPath)) + return; + path = (IndexPath*)sortedPath; + if (path->indexinfo) { + info->ncols = path->indexinfo->nkeycolumns; + info->keys = path->indexinfo->indexkeys; + nTups = path->path.rows; + totalTups = path->indexinfo->tuples; + } else { + /* avoid core */ + return; + } + + /* get all restrictInfo */ + if (!path->indexinfo->rel || !path->indexinfo->rel->baserestrictinfo) + return; + List* restricts = path->indexinfo->rel->baserestrictinfo; + ListCell* lc = NULL; + foreach (lc, restricts) { + /* it should only be RestrictInfo */ + if (IsA(lfirst(lc), RestrictInfo)) { + RestrictInfo* resinfo = (RestrictInfo*)lfirst(lc); + /* has cols not in indexkey */ + if (is_indexpath_has_unkey_quals((Node*)(resinfo->clause), (void*)info)) { + /* + * has sonme cols not in index key ,we should set fraction with new functinon + * fracton is (1 - ntuples/[total-tuple-in-index] + nlimit/[total-tuple-in-index]) + */ + *fraction = (totalTups - nTups + limit_tuples) / totalTups; + + break; + } + } + } +} #endif diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h index 730e9f0b8f9c50a6167b9dc528158d4bd1b4ce60..598853686daee4fbd87989710181357588b19430 100644 --- a/src/include/nodes/plannodes.h +++ b/src/include/nodes/plannodes.h @@ -1766,5 +1766,10 @@ typedef struct AssertOp { List *errmessage; /* error message */ } AssertOp; #endif /* USE_SPQ */ +typedef struct IndexPathKeyInfo { + int ncols; + int* keys; +} IndexPathKeyInfo; + #endif /* PLANNODES_H */ diff --git a/src/include/optimizer/pathnode.h b/src/include/optimizer/pathnode.h index 8cce2273ad340b9a2ed71c798b852b79f3a46249..fa181fed59a8879c43a1a1219952054724f2c6d1 100644 --- a/src/include/optimizer/pathnode.h +++ b/src/include/optimizer/pathnode.h @@ -146,6 +146,7 @@ extern void add_mergejoin_path(PlannerInfo* root, RelOptInfo* joinrel, JoinType Distribution* target_distribution); extern bool equal_distributekey(PlannerInfo* root, List* distribute_key1, List* distribute_key2); extern bool judge_node_compatible(PlannerInfo* root, Node* n1, Node* n2); +extern void change_sortedpath_fraction(Path* sortedPath, double* fraction, double limit_tuples); extern List* build_superset_keys_for_rel( PlannerInfo* root, RelOptInfo* rel, RelOptInfo* outerrel, RelOptInfo* innerrel, JoinType jointype); extern List* remove_duplicate_superset_keys(List* join_dis_key_list); diff --git a/src/include/optimizer/planner.h b/src/include/optimizer/planner.h index 1c7800c81844e00c7412f17f4e01f0c4cdc5d29d..3b1c98a4fb132faeee09dc05301074b016dab584 100644 --- a/src/include/optimizer/planner.h +++ b/src/include/optimizer/planner.h @@ -71,6 +71,8 @@ extern bool is_dummy_plan(Plan* plan); extern bool is_single_baseresult_plan(Plan* plan); +extern bool is_indexpath_has_unkey_quals(Node* node, void* context); + extern Expr* expression_planner(Expr* expr); extern Expr *preprocess_phv_expression(PlannerInfo *root, Expr *expr); diff --git a/src/include/utils/guc.h b/src/include/utils/guc.h index ff38a9ca2c9f9069ca883731861d7a2deebb787a..394bd7734c4c9dfcd5d8fad0d6f642dca22fa1a1 100755 --- a/src/include/utils/guc.h +++ b/src/include/utils/guc.h @@ -421,7 +421,8 @@ typedef enum { PREDPUSH_SAME_LEVEL = (1 << 13), /* predpush same level */ PARTITION_FDW_ON = (1 << 14), /* support create foreign table on partitioned table */ DISABLE_BITMAP_COST_WITH_LOSSY_PAGES = (1 << 15), /* stop computing bitmap path cost with lossy pages */ - EXTRACT_PUSHDOWN_OR_CLAUSE = (1 << 16) /* Extract restriction OR clauses. */ + EXTRACT_PUSHDOWN_OR_CLAUSE = (1 << 16), /* Extract restriction OR clauses. */ + SORTED_INDEX_PATH_OPT = (1 << 17) /* sorted index path optimize in order by limt case */ } sql_beta_param; typedef enum { diff --git a/src/test/regress/expected/plan_hint.out b/src/test/regress/expected/plan_hint.out index 0127a8fef77cd29d8312b770b0f31a120ae3ca0f..25bb4ea969160686ba66e160ba315d53f31bc25a 100755 --- a/src/test/regress/expected/plan_hint.out +++ b/src/test/regress/expected/plan_hint.out @@ -2564,6 +2564,31 @@ explain (analyse,timing off,costs off) create table tb_create_merge_append6 as ( --?.* (21 rows) +--bugfix and add guc +set sql_beta_feature = sorted_index_path_opt; +create table test_pl2(a int primary key ,b int); +NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "test_pl2_pkey" for table "test_pl2" +insert into test_pl2 select generate_series(1,30000), generate_series(1,30000); +insert into test_pl2 select generate_series(30001,30300), 1000; +explain (costs off) select * from test_pl2 where b = 1000 order by a limit 20; + QUERY PLAN +---------------------------------- + Limit + -> Sort + Sort Key: a + -> Seq Scan on test_pl2 + Filter: (b = 1000) +(5 rows) + +set sql_beta_feature = default; +explain (costs off) select * from test_pl2 where b = 1000 order by a limit 20; + QUERY PLAN +-------------------------------------------------- + Limit + -> Index Scan using test_pl2_pkey on test_pl2 + Filter: (b = 1000) +(3 rows) + drop view hint_view_1; drop view hint_view_2; drop view hint_view_3; @@ -2585,8 +2610,9 @@ drop table hint_t4; drop table hint_t5; drop table hint_vec; drop schema plan_hint cascade; -NOTICE: drop cascades to 4 other objects +NOTICE: drop cascades to 5 other objects DETAIL: drop cascades to table src drop cascades to table subpartition_hash_hash drop cascades to table partition_range drop cascades to table tb_create_merge_append6 +drop cascades to table test_pl2 diff --git a/src/test/regress/sql/plan_hint.sql b/src/test/regress/sql/plan_hint.sql index 6fff38948d2190b9fb4f0c1c4a4d6a936429ed26..517c0b10642def58e61135898bfcc2ba720bbbc4 100644 --- a/src/test/regress/sql/plan_hint.sql +++ b/src/test/regress/sql/plan_hint.sql @@ -864,6 +864,16 @@ explain (analyse,timing off,costs off) create table tb_create_merge_append6 as ( limit 100 offset 10 ); +--bugfix and add guc +set sql_beta_feature = sorted_index_path_opt; + +create table test_pl2(a int primary key ,b int); +insert into test_pl2 select generate_series(1,30000), generate_series(1,30000); +insert into test_pl2 select generate_series(30001,30300), 1000; +explain (costs off) select * from test_pl2 where b = 1000 order by a limit 20; +set sql_beta_feature = default; +explain (costs off) select * from test_pl2 where b = 1000 order by a limit 20; + drop view hint_view_1; drop view hint_view_2; drop view hint_view_3;