diff --git a/src/bin/gs_guc/cluster_guc.conf b/src/bin/gs_guc/cluster_guc.conf index 0a249d265..74f4bbd00 100755 --- a/src/bin/gs_guc/cluster_guc.conf +++ b/src/bin/gs_guc/cluster_guc.conf @@ -653,7 +653,7 @@ enable_auto_explain|bool|0,0|NULL|NULL| auto_explain_level|enum|off,log,notice|NULL|NULL| cost_weight_index|real|1e-10,1e+10|NULL|NULL| default_limit_rows|real|-100,1.79769e+308|NULL|NULL| -sql_beta_feature|enum|sel_semi_poisson,sel_expr_instr,param_path_gen,rand_cost_opt,param_path_opt,page_est_opt,none|NULL|NULL| +sql_beta_feature|enum|no_unique_index_first,sel_semi_poisson,sel_expr_instr,param_path_gen,rand_cost_opt,param_path_opt,page_est_opt,none|NULL|NULL| xlog_idle_flushes_before_sleep|int64|0,576460752303423487|NULL|NULL| wal_writer_cpu|int|-1,2147483647|NULL|NULL| wal_file_init_num|int|1,2147483647|NULL|NULL| diff --git a/src/common/backend/utils/misc/guc.cpp b/src/common/backend/utils/misc/guc.cpp index cefe68296..95af85b77 100644 --- a/src/common/backend/utils/misc/guc.cpp +++ b/src/common/backend/utils/misc/guc.cpp @@ -993,6 +993,7 @@ static const struct config_enum_entry sql_beta_options[] = { {"rand_cost_opt", RAND_COST_OPT, false}, {"page_est_opt", PAGE_EST_OPT, false}, {"param_path_opt", PARAM_PATH_OPT, false}, + {"no_unique_index_first", NO_UNIQUE_INDEX_FIRST, false}, {NULL, 0, false} }; diff --git a/src/gausskernel/optimizer/path/costsize.cpp b/src/gausskernel/optimizer/path/costsize.cpp index 406c8d1ba..362003c00 100644 --- a/src/gausskernel/optimizer/path/costsize.cpp +++ b/src/gausskernel/optimizer/path/costsize.cpp @@ -1246,6 +1246,13 @@ void cost_index(IndexPath* path, PlannerInfo* root, double loop_count) min_IO_cost = 0; } + /* + * When database keep running without vacuum, the number of relpages may inflate quickly + * and finally cause min_IO_cost overestimated. So, adjust min_IO_cost to ensure + * min_IO_cost < max_IO_cost. + */ + min_IO_cost = Min(min_IO_cost, max_IO_cost); + ereport(DEBUG2, (errmodule(MOD_OPT), errmsg("Computing IndexScanCost(loop_count = 1): min_pages_fetched: %lf, min_IO_cost: %lf", diff --git a/src/gausskernel/optimizer/path/es_selectivity.cpp b/src/gausskernel/optimizer/path/es_selectivity.cpp index 40870edcf..bb587053e 100644 --- a/src/gausskernel/optimizer/path/es_selectivity.cpp +++ b/src/gausskernel/optimizer/path/es_selectivity.cpp @@ -30,6 +30,7 @@ const int TOW_MEMBERS = 2; ES_SELECTIVITY::ES_SELECTIVITY() : es_candidate_list(NULL), + es_candidate_saved(NULL), unmatched_clause_group(NULL), root(NULL), sjinfo(NULL), @@ -41,6 +42,63 @@ ES_SELECTIVITY::ES_SELECTIVITY() ES_SELECTIVITY::~ES_SELECTIVITY() {} +bool ES_SELECTIVITY::ContainIndexCols(const es_candidate* es, const IndexOptInfo* index) const +{ + for (int pos = 0; pos < index->ncolumns; pos++) { + int indexAttNum = index->indexkeys[pos]; + /* + * Notice: indexAttNum can be negative. Some indexAttNums of junk column may be negative + * since they are located before the first visible column. for example, the indexAttNum + * of 'oid' column in system table 'pg_class' is -2. + */ + if (indexAttNum >= 0 && !bms_is_member(indexAttNum, es->left_attnums)) + return false; + } + + return true; +} + +bool ES_SELECTIVITY::MatchUniqueIndex(const es_candidate* es) const +{ + ListCell* lci = NULL; + foreach (lci, es->left_rel->indexlist) { + IndexOptInfo* indexToMatch = (IndexOptInfo*)lfirst(lci); + if (indexToMatch->relam == BTREE_AM_OID && indexToMatch->unique + && ContainIndexCols(es, indexToMatch)) { + return true; + } + } + + return false; +} + +/* + * check whether the equality constraints match an unique index. + * We know the result only has one row if finding a matched unique index. + */ +void ES_SELECTIVITY::CalSelWithUniqueIndex(Selectivity &result) +{ + List* es_candidate_used = NULL; + ListCell* l = NULL; + foreach(l, es_candidate_list) { + es_candidate* temp = (es_candidate*)lfirst(l); + if (temp->tag == ES_EQSEL && MatchUniqueIndex(temp) && + temp->left_rel && temp->left_rel->tuples >= 1.0) { + result *= 1.0 / temp->left_rel->tuples; + es_candidate_used = lappend(es_candidate_used, temp); + } + } + + /* + * Finally, we need to delete es_candidates which have already used. The rests es_candidates + * will calculate with statistic info. + */ + es_candidate_saved = es_candidate_list; + es_candidate_list = list_difference(es_candidate_list, es_candidate_used); + + list_free(es_candidate_used); +} + /* * @brief Main entry for using extended statistic to calculate selectivity * root_input can only be NULL when processing group by clauses @@ -62,6 +120,12 @@ Selectivity ES_SELECTIVITY::calculate_selectivity(PlannerInfo* root_input, List* group_clauselist(origin_clauses); } + /* + * Before reading statistic, We check whether the equality constraints match an + * unique index. We know the result only has one row if finding a matched unique index. + */ + CalSelWithUniqueIndex(result); + /* read statistic */ read_statistic(); @@ -91,6 +155,8 @@ Selectivity ES_SELECTIVITY::calculate_selectivity(PlannerInfo* root_input, List* } } + es_candidate_list = es_candidate_saved; + /* free memory, but unmatched_clause_group need to be free manually */ clear(); @@ -589,7 +655,7 @@ static bool ClauseIsLegal(es_type type, const Node* left, const Node* right, int /* check clause type */ switch (type) { case ES_EQSEL: - if (!IsA(left, Const) && !IsA(right, Const)) + if (!IsA(left, Const) && !IsA(right, Const) && !IsA(left, Param) && !IsA(right, Param)) return false; else if (IsA(left, Const) && ((Const*)left)->constisnull) return false; @@ -1251,7 +1317,7 @@ int ES_SELECTIVITY::read_attnum(Node* node) const attnum = var->varattno; if (attnum <= 0) attnum = -1; - } else if (IsA(node, Const)) + } else if (IsA(node, Const) || IsA(node, Param)) attnum = 0; return attnum; diff --git a/src/gausskernel/optimizer/path/indxpath.cpp b/src/gausskernel/optimizer/path/indxpath.cpp index 53aa5e03b..710abf0fc 100644 --- a/src/gausskernel/optimizer/path/indxpath.cpp +++ b/src/gausskernel/optimizer/path/indxpath.cpp @@ -27,6 +27,7 @@ #include "catalog/pg_operator.h" #include "catalog/pg_opfamily.h" #include "catalog/pg_type.h" +#include "catalog/pg_proc.h" #include "nodes/makefuncs.h" #include "optimizer/clauses.h" #include "optimizer/cost.h" @@ -725,6 +726,67 @@ static inline bool index_relation_has_bucket(IndexOptInfo* index) return hasBucket; } +inline bool IsEqRestrict(const RestrictInfo* rinfo) +{ + Expr* clause = rinfo->clause; + return is_opclause(clause) && get_oprrest(((OpExpr*)clause)->opno) == EQSELRETURNOID; +} + +/* + * Check whether the indexqualcols in indexpath contain the given attNum and the + * constraint condition on this attNum is equality constraints. + */ +inline bool HasAttNumAndEqRestrict(const IndexPath* newPath, const int attNum) +{ + ListCell* lc1 = NULL; + ListCell* lc2 = NULL; + IndexOptInfo* newPathIndex = (IndexOptInfo*)newPath->indexinfo; + forboth (lc1, newPath->indexqualcols, lc2, newPath->indexquals) { + int i = lfirst_int(lc1); + RestrictInfo* rinfo = (RestrictInfo*)lfirst(lc2); + if (newPathIndex->indexkeys[i] == attNum && IsEqRestrict(rinfo)) + return true; + } + return false; +} + +/* + * Check whether index path contain all the index columns and the constraint conditions + * are equality constraints. + */ +inline bool ContainAllColsAndEqRestrict(const IndexPath* newPath, const IndexOptInfo* index) +{ + for (int pos = 0; pos < index->ncolumns; pos++) { + if (!HasAttNumAndEqRestrict(newPath, index->indexkeys[pos])) + return false; + } + return true; +} + +/* + * For the given index, we want to mark whether the index contains the columns come from an + * unique index and the constraint conditions on these columns are equality constraints. This + * mark will be used in unique index first rule during path generation. + */ +void MarkUniqueIndexFirstRule(const RelOptInfo* rel, const IndexOptInfo* index, List* result) +{ + if (!ENABLE_SQL_BETA_FEATURE(NO_UNIQUE_INDEX_FIRST) && index->relam == BTREE_AM_OID) { + ListCell* lcr = NULL; + foreach (lcr, result) { + IndexPath* newPath = (IndexPath*)lfirst(lcr); + ListCell* lci = NULL; + foreach (lci, rel->indexlist) { + IndexOptInfo* indexToMatch = (IndexOptInfo*)lfirst(lci); + if (indexToMatch->relam == BTREE_AM_OID && indexToMatch->unique && + ContainAllColsAndEqRestrict(newPath, indexToMatch)) { + newPath->rulesforindexgen |= BTREE_INDEX_CONTAIN_UNIQUE_COLS; + break; + } + } + } + } +} + /* * build_index_paths * Given an index and a set of index clauses for it, construct zero @@ -964,6 +1026,13 @@ static List* build_index_paths(PlannerInfo* root, RelOptInfo* rel, IndexOptInfo* } } + /* + * 6. Mark whether unique index fisrt rule satisfied in current btree index path. + * The rules will be used for selecting paths. We will check whether current index path + * contains a unique btree columns and the constraint conditions are equality constraints. + */ + MarkUniqueIndexFirstRule(rel, index, result); + return result; } diff --git a/src/gausskernel/optimizer/util/pathnode.cpp b/src/gausskernel/optimizer/util/pathnode.cpp index 9b56d887f..8ae8e4ef8 100644 --- a/src/gausskernel/optimizer/util/pathnode.cpp +++ b/src/gausskernel/optimizer/util/pathnode.cpp @@ -253,6 +253,134 @@ PathCostComparison compare_join_single_node_distribution(Path* path1, Path* path return COSTS_DIFFERENT; } +inline bool IsSeqScanPath(const Path* path) +{ + return path->pathtype == T_SeqScan; +} + +inline bool IsBtreeIndexPath(const Path* path) +{ + return path->type == T_IndexPath && + ((IndexPath*)path)->indexinfo->relam == BTREE_AM_OID; +} + +inline bool AreTwoBtreeIdxPaths(const Path* path1, const Path* path2) +{ + return IsBtreeIndexPath(path1) && IsBtreeIndexPath(path2); +} + +inline bool IsBtreeIdxAndSeqPath(const Path* path1, const Path* path2) +{ + return (IsBtreeIndexPath(path1) && IsSeqScanPath(path2)) || + (IsBtreeIndexPath(path2) && IsSeqScanPath(path1)); +} + +inline bool IsParamPath(const Path* path) +{ + return path->param_info != NULL; +} + +inline bool BothParamPathOrBothNot(const Path* path1, const Path* path2) +{ + return (IsParamPath(path1) && IsParamPath(path2)) || + (!IsParamPath(path1) && !IsParamPath(path2)); +} + +inline bool ContainUniqueCols(const IndexPath* path) +{ + return path->rulesforindexgen & BTREE_INDEX_CONTAIN_UNIQUE_COLS; +} + +/* + * The main entry for unique index first rule. + * In this rule, we check two aspects: + * 1. For Btree index pathA and pathB, pathA contains a unique btree columns and the constraint + * conditions are equality constraints. We prefer pathA. + * Notice: Only consider unique index first rule when the two index paths are both parameterized path + * or both not. + * 2. For Btree index pathA and SeqScan pathB, pathA contains a unique btree columns and the constraint + * conditions are equality constraints. We prefer pathA. + * Notice: This rule is vaild when Btree index pathA is unparameterized path. + */ +bool ImplementUniqueIndexRule(const Path* path1, const Path* path2, PathCostComparison &cost_comparison) +{ + /* Check the first aspect */ + if (AreTwoBtreeIdxPaths(path1, path2) && BothParamPathOrBothNot(path1, path2)) { + bool path1ContainUniqueCols = ContainUniqueCols((IndexPath*)path1); + bool path2ContainUniqueCols = ContainUniqueCols((IndexPath*)path2); + /* Compare with 1 to verify whether one path satisfy unique index first rule and another don't. */ + if (path1ContainUniqueCols + path2ContainUniqueCols == 1) { + const int suppressionParam = g_instance.cost_cxt.disable_cost_enlarge_factor; + if (path1ContainUniqueCols == true && path1->total_cost < suppressionParam * path2->total_cost) { + cost_comparison = COSTS_BETTER1; + return true; + } else if (path2ContainUniqueCols == true && path2->total_cost < suppressionParam * path1->total_cost) { + cost_comparison = COSTS_BETTER2; + return true; + } + } + return false; + } + + /* Check the second aspect */ + if (IsBtreeIdxAndSeqPath(path1, path2)) { + const int suppressionParam = g_instance.cost_cxt.disable_cost_enlarge_factor; + if (IsSeqScanPath(path1) && !IsParamPath(path2) && ContainUniqueCols((IndexPath*)path2) && + path2->total_cost < suppressionParam * path1->total_cost) { + cost_comparison = COSTS_BETTER2; + return true; + } else if (IsSeqScanPath(path2) && !IsParamPath(path1) && ContainUniqueCols((IndexPath*)path1) && + path1->total_cost < suppressionParam * path2->total_cost) { + cost_comparison = COSTS_BETTER1; + return true; + } + return false; + } + + return false; +} + +void DebugPrintUniqueIndexFirstInfo(const Path* path1, const Path* path2, const PathCostComparison &cost_comparison) +{ + char* preferIndexName = NULL; + char* ruledOutIndexName = NULL; + + if (cost_comparison == COSTS_BETTER1) { + preferIndexName = get_rel_name(((IndexPath*)path1)->indexinfo->indexoid); + ruledOutIndexName = IsBtreeIndexPath(path2) ? get_rel_name(((IndexPath*)path2)->indexinfo->indexoid) : NULL; + } else { + preferIndexName = get_rel_name(((IndexPath*)path2)->indexinfo->indexoid); + ruledOutIndexName = IsBtreeIndexPath(path1) ? get_rel_name(((IndexPath*)path1)->indexinfo->indexoid) : NULL; + } + + /* ruledOutIndexName = NULL means the ruled out path is a seqscan path. */ + if (ruledOutIndexName == NULL) { + ereport(DEBUG1, + (errmodule(MOD_OPT), + errmsg("Implement Unique Index rule in selecting path: prefer to use index: %s, rule out seqscan.", + preferIndexName))); + } else { + ereport(DEBUG1, + (errmodule(MOD_OPT), + errmsg("Implement Unique Index rule in selecting path: prefer to use index: %s, rule out index: %s.", + preferIndexName, ruledOutIndexName))); + } + + pfree_ext(preferIndexName); + pfree_ext(ruledOutIndexName); +} + +/* Check whether unique index first rule can be used */ +bool CheckUniqueIndexFirstRule(const Path* path1, const Path* path2, PathCostComparison &cost_comparison) +{ + if (!ENABLE_SQL_BETA_FEATURE(NO_UNIQUE_INDEX_FIRST) && ImplementUniqueIndexRule(path1, path2, cost_comparison)) { + if (log_min_messages <= DEBUG1) + DebugPrintUniqueIndexFirstInfo(path1, path2, cost_comparison); + return true; + } + return false; +} + /* * compare_path_costs_fuzzily * Compare the costs of two paths to see if either can be said to @@ -282,8 +410,18 @@ PathCostComparison compare_path_costs_fuzzily(Path* path1, Path* path2, double f else if (path1->hint_value < path2->hint_value) return COSTS_BETTER2; + PathCostComparison cost_comparison; + + /* + * For index paths, we check if rules can be used to filter. + * Here, we tend to select path containing unique index columns. + */ + if (CheckUniqueIndexFirstRule(path1, path2, cost_comparison)) { + return cost_comparison; + } + /* dn gather RBO */ - PathCostComparison cost_comparison = compare_join_single_node_distribution(path1, path2); + cost_comparison = compare_join_single_node_distribution(path1, path2); if (cost_comparison != COSTS_DIFFERENT) { return cost_comparison; } diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h index bb24a0e19..c587123f8 100644 --- a/src/include/nodes/relation.h +++ b/src/include/nodes/relation.h @@ -31,6 +31,11 @@ */ typedef enum CostSelector { STARTUP_COST, TOTAL_COST } CostSelector; +/* Different rules are used for path generation */ +typedef enum { + NO_PATH_GEN_RULE = 0, + BTREE_INDEX_CONTAIN_UNIQUE_COLS = 1 /* an equivalence constraint btree index scan contains unique cols */ +} RulesForPathGen; /* * The cost estimate produced by cost_qual_eval() includes both a one-time * (startup) cost, and a per-tuple cost. @@ -971,6 +976,10 @@ typedef struct Path { * ORDER BY expression is meant to be used with. (There is no restriction * on which index column each ORDER BY can be used with.) * + * 'rulesforindexgen' is a bitmapset. It is used for recording some rules which + * are satisfied in current index path. These recorded rules will be used for + * filtering paths. We can consider it as the supplement of CBO (cost based optimize). + * * 'indexscandir' is one of: * ForwardScanDirection: forward scan of an ordered index * BackwardScanDirection: backward scan of an ordered index @@ -993,6 +1002,7 @@ typedef struct IndexPath { List* indexqualcols; List* indexorderbys; List* indexorderbycols; + int rulesforindexgen = NO_PATH_GEN_RULE; ScanDirection indexscandir; Cost indextotalcost; Selectivity indexselectivity; diff --git a/src/include/optimizer/cost.h b/src/include/optimizer/cost.h index 48c65ee66..373360ef7 100644 --- a/src/include/optimizer/cost.h +++ b/src/include/optimizer/cost.h @@ -251,6 +251,7 @@ struct es_candidate { class ES_SELECTIVITY : public BaseObject { public: List* es_candidate_list; + List* es_candidate_saved; /* temporarily save es_candidate_list */ List* unmatched_clause_group; PlannerInfo* root; /* root from input */ SpecialJoinInfo* sjinfo; /* sjinfo from input */ @@ -289,8 +290,10 @@ private: Selectivity cal_eqjoinsel_semi(es_candidate* es, RelOptInfo* inner_rel, bool inner_on_left); void cal_stadistinct_eqsel(es_candidate* es); bool cal_stadistinct_eqjoinsel(es_candidate* es); + void CalSelWithUniqueIndex(Selectivity &result); void clear_extended_stats(ExtendedStats* extended_stats) const; void clear_extended_stats_list(List* stats_list) const; + bool ContainIndexCols(const es_candidate* es, const IndexOptInfo* index) const; ExtendedStats* copy_stats_ptr(ListCell* l) const; void debug_print(); double estimate_local_numdistinct(es_bucketsize* bucket, bool left, Path* path); @@ -302,6 +305,7 @@ private: Bitmapset* make_attnums_by_clause_map(es_candidate* es, Bitmapset* attnums, bool left) const; void match_extended_stats(es_candidate* es, List* stats_list, bool left); bool match_pseudo_clauselist(List* clauses, es_candidate* es, List* origin_clause); + bool MatchUniqueIndex(const es_candidate* es) const; void modify_distinct_by_possion_model(es_candidate* es, bool left, SpecialJoinInfo* sjinfo) const; char* print_expr(const Node* expr, const List* rtable) const; void print_rel(RangeTblEntry* rel) const; diff --git a/src/include/utils/guc.h b/src/include/utils/guc.h index b63b5d43b..f12c82161 100644 --- a/src/include/utils/guc.h +++ b/src/include/utils/guc.h @@ -356,7 +356,8 @@ typedef enum { PARAM_PATH_GEN = 4, /* Parametrized Path Generation */ RAND_COST_OPT = 8, /* Optimizing sc_random_page_cost */ PARAM_PATH_OPT = 16, /* Parametrized Path Optimization. */ - PAGE_EST_OPT = 32 /* More accurate (rowstored) index pages estimation */ + PAGE_EST_OPT = 32, /* More accurate (rowstored) index pages estimation */ + NO_UNIQUE_INDEX_FIRST = 64 /* use unique index first rule in path generation */ } sql_beta_param; #define ENABLE_PRED_PUSH(root) \ diff --git a/src/test/regress/expected/mulcolpk.out b/src/test/regress/expected/mulcolpk.out new file mode 100755 index 000000000..bf37acb54 --- /dev/null +++ b/src/test/regress/expected/mulcolpk.out @@ -0,0 +1,36 @@ +create schema mulcolpk; +set current_schema to mulcolpk; +create table mulcolpk (a int, b int); +insert into mulcolpk values (generate_series(0, 0), generate_series(1, 90)); +insert into mulcolpk values (generate_series(1, 10), generate_series(0, 0)); +analyze mulcolpk; +--we just test row estimate in index path. +set enable_seqscan = off; +set enable_bitmapscan = off; +set plan_cache_mode = force_generic_plan; +--1. create index +create index mulcolpk_idx on mulcolpk(a, b); +explain select * from mulcolpk where a = 0 and b = 0; + QUERY PLAN +---------------------------------------------------------------------------------- + [Bypass] + Index Only Scan using mulcolpk_idx on mulcolpk (cost=0.00..8.43 rows=9 width=8) + Index Cond: ((a = 0) AND (b = 0)) +(3 rows) + +--2. create unique index +drop index mulcolpk_idx; +create unique index mulcolpk_idx on mulcolpk(a, b); +explain select * from mulcolpk where a = 0 and b = 0; + QUERY PLAN +---------------------------------------------------------------------------------- + [Bypass] + Index Only Scan using mulcolpk_idx on mulcolpk (cost=0.00..8.27 rows=1 width=8) + Index Cond: ((a = 0) AND (b = 0)) +(3 rows) + +reset plan_cache_mode; +reset enable_bitmapscan; +reset enable_seqscan; +drop schema mulcolpk cascade; +NOTICE: drop cascades to table mulcolpk diff --git a/src/test/regress/expected/pbe_partition.out b/src/test/regress/expected/pbe_partition.out new file mode 100755 index 000000000..79cde9fb3 --- /dev/null +++ b/src/test/regress/expected/pbe_partition.out @@ -0,0 +1,600 @@ +SET plan_cache_mode = force_generic_plan; +-- create range_partition table. +CREATE TABLE partition_scan1(a int, b int) +PARTITION BY RANGE (a) +( + PARTITION P1 VALUES LESS THAN(10), + PARTITION P2 VALUES LESS THAN(20), + PARTITION P3 VALUES LESS THAN(30), + PARTITION P4 VALUES LESS THAN(40) +); +CREATE UNIQUE INDEX index_on_partition_scan1 ON partition_scan1(a) LOCAL; +insert into partition_scan1 values(generate_series(1,39,1), generate_series(1,39,1)); +-- 等于param +prepare p1(int) as SELECT * FROM partition_scan1 s1 where s1.a = $1 ORDER BY s1.a; +explain (costs off) execute p1(10); + QUERY PLAN +----------------------------------------------------------------------------------- + Partition Iterator + Iterations: 4 + -> Partitioned Index Scan using index_on_partition_scan1 on partition_scan1 s1 + Index Cond: (a = $1) + Selected Partitions: 1..4 +(5 rows) + +execute p1(10); + a | b +----+---- + 10 | 10 +(1 row) + +--大于param +prepare p2(int) as SELECT * FROM partition_scan1 s1 where s1.a >$1 ORDER BY s1.a; +explain (costs off) execute p2(35); + QUERY PLAN +-------------------------------------------------------- + Sort + Sort Key: a + -> Partition Iterator + Iterations: 4 + -> Partitioned Seq Scan on partition_scan1 s1 + Filter: (a > $1) + Selected Partitions: 1..4 +(7 rows) + +execute p2(35); + a | b +----+---- + 36 | 36 + 37 | 37 + 38 | 38 + 39 | 39 +(4 rows) + +--小于param +prepare p3(int) as SELECT * FROM partition_scan1 s1 where s1.a <$1 ORDER BY s1.a; +explain (costs off) execute p3(35); + QUERY PLAN +-------------------------------------------------------- + Sort + Sort Key: a + -> Partition Iterator + Iterations: 4 + -> Partitioned Seq Scan on partition_scan1 s1 + Filter: (a < $1) + Selected Partitions: 1..4 +(7 rows) + +execute p3(35); + a | b +----+---- + 1 | 1 + 2 | 2 + 3 | 3 + 4 | 4 + 5 | 5 + 6 | 6 + 7 | 7 + 8 | 8 + 9 | 9 + 10 | 10 + 11 | 11 + 12 | 12 + 13 | 13 + 14 | 14 + 15 | 15 + 16 | 16 + 17 | 17 + 18 | 18 + 19 | 19 + 20 | 20 + 21 | 21 + 22 | 22 + 23 | 23 + 24 | 24 + 25 | 25 + 26 | 26 + 27 | 27 + 28 | 28 + 29 | 29 + 30 | 30 + 31 | 31 + 32 | 32 + 33 | 33 + 34 | 34 +(34 rows) + +-- 大于等于param +prepare p4(int) as SELECT * FROM partition_scan1 s1 where s1.a >=$1 ORDER BY s1.a; +explain (costs off) execute p4(35); + QUERY PLAN +-------------------------------------------------------- + Sort + Sort Key: a + -> Partition Iterator + Iterations: 4 + -> Partitioned Seq Scan on partition_scan1 s1 + Filter: (a >= $1) + Selected Partitions: 1..4 +(7 rows) + +execute p4(35); + a | b +----+---- + 35 | 35 + 36 | 36 + 37 | 37 + 38 | 38 + 39 | 39 +(5 rows) + +-- 小于等于param +prepare p5(int) as SELECT * FROM partition_scan1 s1 where s1.a <=$1 ORDER BY s1.a; +explain (costs off) execute p5(35); + QUERY PLAN +-------------------------------------------------------- + Sort + Sort Key: a + -> Partition Iterator + Iterations: 4 + -> Partitioned Seq Scan on partition_scan1 s1 + Filter: (a <= $1) + Selected Partitions: 1..4 +(7 rows) + +execute p5(35); + a | b +----+---- + 1 | 1 + 2 | 2 + 3 | 3 + 4 | 4 + 5 | 5 + 6 | 6 + 7 | 7 + 8 | 8 + 9 | 9 + 10 | 10 + 11 | 11 + 12 | 12 + 13 | 13 + 14 | 14 + 15 | 15 + 16 | 16 + 17 | 17 + 18 | 18 + 19 | 19 + 20 | 20 + 21 | 21 + 22 | 22 + 23 | 23 + 24 | 24 + 25 | 25 + 26 | 26 + 27 | 27 + 28 | 28 + 29 | 29 + 30 | 30 + 31 | 31 + 32 | 32 + 33 | 33 + 34 | 34 + 35 | 35 +(35 rows) + +-- 等于expr +prepare p6(int,int) as SELECT * FROM partition_scan1 s1 where s1.a = $1+$2+1 ORDER BY s1.a; +explain (costs off) execute p6(10,10); + QUERY PLAN +----------------------------------------------------------------------------------- + Partition Iterator + Iterations: 4 + -> Partitioned Index Scan using index_on_partition_scan1 on partition_scan1 s1 + Index Cond: (a = (($1 + $2) + 1)) + Selected Partitions: 1..4 +(5 rows) + +execute p6(10,10); + a | b +----+---- + 21 | 21 +(1 row) + +--大于expr +prepare p7(int,int) as SELECT * FROM partition_scan1 s1 where s1.a > $1+$2+1 ORDER BY s1.a; +explain (costs off) execute p7(10,10); + QUERY PLAN +----------------------------------------------------------------------------- + Sort + Sort Key: a + -> Partition Iterator + Iterations: 4 + -> Partitioned Bitmap Heap Scan on partition_scan1 s1 + Recheck Cond: (a > (($1 + $2) + 1)) + Selected Partitions: 1..4 + -> Partitioned Bitmap Index Scan on index_on_partition_scan1 + Index Cond: (a > (($1 + $2) + 1)) + Selected Partitions: 1..4 +(10 rows) + +execute p7(10,10); + a | b +----+---- + 22 | 22 + 23 | 23 + 24 | 24 + 25 | 25 + 26 | 26 + 27 | 27 + 28 | 28 + 29 | 29 + 30 | 30 + 31 | 31 + 32 | 32 + 33 | 33 + 34 | 34 + 35 | 35 + 36 | 36 + 37 | 37 + 38 | 38 + 39 | 39 +(18 rows) + +--小于expr +prepare p8(int,int) as SELECT * FROM partition_scan1 s1 where s1.a < $1+$2+1 ORDER BY s1.a; +explain (costs off) execute p8(10,10); + QUERY PLAN +----------------------------------------------------------------------------- + Sort + Sort Key: a + -> Partition Iterator + Iterations: 4 + -> Partitioned Bitmap Heap Scan on partition_scan1 s1 + Recheck Cond: (a < (($1 + $2) + 1)) + Selected Partitions: 1..4 + -> Partitioned Bitmap Index Scan on index_on_partition_scan1 + Index Cond: (a < (($1 + $2) + 1)) + Selected Partitions: 1..4 +(10 rows) + +execute p8(10,10); + a | b +----+---- + 1 | 1 + 2 | 2 + 3 | 3 + 4 | 4 + 5 | 5 + 6 | 6 + 7 | 7 + 8 | 8 + 9 | 9 + 10 | 10 + 11 | 11 + 12 | 12 + 13 | 13 + 14 | 14 + 15 | 15 + 16 | 16 + 17 | 17 + 18 | 18 + 19 | 19 + 20 | 20 +(20 rows) + +-- 大于等于expr +prepare p9(int,int) as SELECT * FROM partition_scan1 s1 where s1.a >= $1+$2+1 ORDER BY s1.a; +explain (costs off) execute p9(10,10); + QUERY PLAN +----------------------------------------------------------------------------- + Sort + Sort Key: a + -> Partition Iterator + Iterations: 4 + -> Partitioned Bitmap Heap Scan on partition_scan1 s1 + Recheck Cond: (a >= (($1 + $2) + 1)) + Selected Partitions: 1..4 + -> Partitioned Bitmap Index Scan on index_on_partition_scan1 + Index Cond: (a >= (($1 + $2) + 1)) + Selected Partitions: 1..4 +(10 rows) + +execute p9(10,10); + a | b +----+---- + 21 | 21 + 22 | 22 + 23 | 23 + 24 | 24 + 25 | 25 + 26 | 26 + 27 | 27 + 28 | 28 + 29 | 29 + 30 | 30 + 31 | 31 + 32 | 32 + 33 | 33 + 34 | 34 + 35 | 35 + 36 | 36 + 37 | 37 + 38 | 38 + 39 | 39 +(19 rows) + +-- 小于等于expr +prepare p10(int,int) as SELECT * FROM partition_scan1 s1 where s1.a <= $1+$2+1 ORDER BY s1.a; +explain (costs off) execute p10(10,10); + QUERY PLAN +----------------------------------------------------------------------------- + Sort + Sort Key: a + -> Partition Iterator + Iterations: 4 + -> Partitioned Bitmap Heap Scan on partition_scan1 s1 + Recheck Cond: (a <= (($1 + $2) + 1)) + Selected Partitions: 1..4 + -> Partitioned Bitmap Index Scan on index_on_partition_scan1 + Index Cond: (a <= (($1 + $2) + 1)) + Selected Partitions: 1..4 +(10 rows) + +execute p10(10,10); + a | b +----+---- + 1 | 1 + 2 | 2 + 3 | 3 + 4 | 4 + 5 | 5 + 6 | 6 + 7 | 7 + 8 | 8 + 9 | 9 + 10 | 10 + 11 | 11 + 12 | 12 + 13 | 13 + 14 | 14 + 15 | 15 + 16 | 16 + 17 | 17 + 18 | 18 + 19 | 19 + 20 | 20 + 21 | 21 +(21 rows) + +--boolexpr_and +prepare p11(int,int) as SELECT * FROM partition_scan1 s1 where s1.a >= $1 and s1.b = $2 ORDER BY s1.a; +explain (costs off) execute p11(10,10); + QUERY PLAN +----------------------------------------------------------------------------- + Sort + Sort Key: a + -> Partition Iterator + Iterations: 4 + -> Partitioned Bitmap Heap Scan on partition_scan1 s1 + Recheck Cond: (a >= $1) + Filter: (b = $2) + Selected Partitions: 1..4 + -> Partitioned Bitmap Index Scan on index_on_partition_scan1 + Index Cond: (a >= $1) + Selected Partitions: 1..4 +(11 rows) + +execute p11(10,10); + a | b +----+---- + 10 | 10 +(1 row) + +prepare p12(int,int) as SELECT * FROM partition_scan1 s1 where s1.a > $1 and s1.b < $2; +explain (costs off) execute p12(10,10); + QUERY PLAN +----------------------------------------------------------------------- + Partition Iterator + Iterations: 4 + -> Partitioned Bitmap Heap Scan on partition_scan1 s1 + Recheck Cond: (a > $1) + Filter: (b < $2) + Selected Partitions: 1..4 + -> Partitioned Bitmap Index Scan on index_on_partition_scan1 + Index Cond: (a > $1) + Selected Partitions: 1..4 +(9 rows) + +prepare p13(int,int) as SELECT * FROM partition_scan1 s1 where s1.a <= $1 and s1.b = $2+1; +explain (costs off) execute p13(10,10); + QUERY PLAN +----------------------------------------------------------------------- + Partition Iterator + Iterations: 4 + -> Partitioned Bitmap Heap Scan on partition_scan1 s1 + Recheck Cond: (a <= $1) + Filter: (b = ($2 + 1)) + Selected Partitions: 1..4 + -> Partitioned Bitmap Index Scan on index_on_partition_scan1 + Index Cond: (a <= $1) + Selected Partitions: 1..4 +(9 rows) + +prepare p131(int,int) as SELECT * FROM partition_scan1 s1 where s1.a < $1+1 and s1.b > $2+1; +explain (costs off) execute p131(10,10); + QUERY PLAN +----------------------------------------------------------------------- + Partition Iterator + Iterations: 4 + -> Partitioned Bitmap Heap Scan on partition_scan1 s1 + Recheck Cond: (a < ($1 + 1)) + Filter: (b > ($2 + 1)) + Selected Partitions: 1..4 + -> Partitioned Bitmap Index Scan on index_on_partition_scan1 + Index Cond: (a < ($1 + 1)) + Selected Partitions: 1..4 +(9 rows) + +--update +prepare p14(int) as UPDATE partition_scan1 set b = b + 10 where a = $1; +explain (costs off) execute p14(10); + QUERY PLAN +-------------------------------------------------------------------------------------- + Update on partition_scan1 + -> Partition Iterator + Iterations: 4 + -> Partitioned Index Scan using index_on_partition_scan1 on partition_scan1 + Index Cond: (a = $1) + Selected Partitions: 1..4 +(6 rows) + +prepare p15(int) as UPDATE partition_scan1 set b = b + 10 where a > $1; +explain (costs off) execute p15(10); + QUERY PLAN +----------------------------------------------------- + Update on partition_scan1 + -> Partition Iterator + Iterations: 4 + -> Partitioned Seq Scan on partition_scan1 + Filter: (a > $1) + Selected Partitions: 1..4 +(6 rows) + +prepare p16(int) as UPDATE partition_scan1 set b = b + 10 where a >= $1+1; +explain (costs off) execute p16(10); + QUERY PLAN +----------------------------------------------------------------------------- + Update on partition_scan1 + -> Partition Iterator + Iterations: 4 + -> Partitioned Bitmap Heap Scan on partition_scan1 + Recheck Cond: (a >= ($1 + 1)) + Selected Partitions: 1..4 + -> Partitioned Bitmap Index Scan on index_on_partition_scan1 + Index Cond: (a >= ($1 + 1)) + Selected Partitions: 1..4 +(9 rows) + +prepare p17(int) as UPDATE partition_scan1 set b = b + 10 where a <= $1; +explain (costs off) execute p17(10); + QUERY PLAN +----------------------------------------------------- + Update on partition_scan1 + -> Partition Iterator + Iterations: 4 + -> Partitioned Seq Scan on partition_scan1 + Filter: (a <= $1) + Selected Partitions: 1..4 +(6 rows) + +prepare p18(int) as UPDATE partition_scan1 set b = b + 10 where a < $1; +explain (costs off) execute p18(10); + QUERY PLAN +----------------------------------------------------- + Update on partition_scan1 + -> Partition Iterator + Iterations: 4 + -> Partitioned Seq Scan on partition_scan1 + Filter: (a < $1) + Selected Partitions: 1..4 +(6 rows) + +prepare p181(int,int) as UPDATE partition_scan1 set b = b + 10 where a < $1 and b < $2; +explain (costs off) execute p181(10,10); + QUERY PLAN +----------------------------------------------------------------------------- + Update on partition_scan1 + -> Partition Iterator + Iterations: 4 + -> Partitioned Bitmap Heap Scan on partition_scan1 + Recheck Cond: (a < $1) + Filter: (b < $2) + Selected Partitions: 1..4 + -> Partitioned Bitmap Index Scan on index_on_partition_scan1 + Index Cond: (a < $1) + Selected Partitions: 1..4 +(10 rows) + +-- delete +prepare p19(int) as DELETE FROM partition_scan1 where a=$1; +explain (costs off) execute p19(1); + QUERY PLAN +-------------------------------------------------------------------------------------- + Delete on partition_scan1 + -> Partition Iterator + Iterations: 4 + -> Partitioned Index Scan using index_on_partition_scan1 on partition_scan1 + Index Cond: (a = $1) + Selected Partitions: 1..4 +(6 rows) + +prepare p20(int) as DELETE FROM partition_scan1 where a>$1; +explain (costs off) execute p20(38); + QUERY PLAN +----------------------------------------------------- + Delete on partition_scan1 + -> Partition Iterator + Iterations: 4 + -> Partitioned Seq Scan on partition_scan1 + Filter: (a > $1) + Selected Partitions: 1..4 +(6 rows) + +prepare p21(int) as DELETE FROM partition_scan1 where a<$1; +explain (costs off) execute p21(3); + QUERY PLAN +----------------------------------------------------- + Delete on partition_scan1 + -> Partition Iterator + Iterations: 4 + -> Partitioned Seq Scan on partition_scan1 + Filter: (a < $1) + Selected Partitions: 1..4 +(6 rows) + +prepare p22(int) as DELETE FROM partition_scan1 where a>$1+1; +explain (costs off) execute p22(37); + QUERY PLAN +----------------------------------------------------------------------------- + Delete on partition_scan1 + -> Partition Iterator + Iterations: 4 + -> Partitioned Bitmap Heap Scan on partition_scan1 + Recheck Cond: (a > ($1 + 1)) + Selected Partitions: 1..4 + -> Partitioned Bitmap Index Scan on index_on_partition_scan1 + Index Cond: (a > ($1 + 1)) + Selected Partitions: 1..4 +(9 rows) + +prepare p23(int) as DELETE FROM partition_scan1 where a<$1+1; +explain (costs off) execute p23(5); + QUERY PLAN +----------------------------------------------------------------------------- + Delete on partition_scan1 + -> Partition Iterator + Iterations: 4 + -> Partitioned Bitmap Heap Scan on partition_scan1 + Recheck Cond: (a < ($1 + 1)) + Selected Partitions: 1..4 + -> Partitioned Bitmap Index Scan on index_on_partition_scan1 + Index Cond: (a < ($1 + 1)) + Selected Partitions: 1..4 +(9 rows) + +prepare p24(int,int) as DELETE FROM partition_scan1 where a>$1 and b>$2; +explain (costs off) execute p24(30,1); + QUERY PLAN +----------------------------------------------------------------------------- + Delete on partition_scan1 + -> Partition Iterator + Iterations: 4 + -> Partitioned Bitmap Heap Scan on partition_scan1 + Recheck Cond: (a > $1) + Filter: (b > $2) + Selected Partitions: 1..4 + -> Partitioned Bitmap Index Scan on index_on_partition_scan1 + Index Cond: (a > $1) + Selected Partitions: 1..4 +(10 rows) + diff --git a/src/test/regress/expected/unique_index_first.out b/src/test/regress/expected/unique_index_first.out new file mode 100755 index 000000000..8828ea295 --- /dev/null +++ b/src/test/regress/expected/unique_index_first.out @@ -0,0 +1,44 @@ +create schema unique_index_first; +set current_schema to unique_index_first; +create table unique_index_first (col1 int, col2 int, col3 int, col4 int, col5 int); +alter table unique_index_first add primary key (col1, col2, col3, col4); +NOTICE: ALTER TABLE / ADD PRIMARY KEY will create implicit index "unique_index_first_pkey" for table "unique_index_first" +create index index_no_unique on unique_index_first using btree(col1, col2, col3, col5); +--1. A btree plan containing unique columns is prefered. +explain (costs off) select * from unique_index_first where col2 = 2 and col4 = 4 and col3 = 3 and col1 = 1; + QUERY PLAN +------------------------------------------------------------------------- + [Bypass] + Index Scan using unique_index_first_pkey on unique_index_first + Index Cond: ((col1 = 1) AND (col2 = 2) AND (col3 = 3) AND (col4 = 4)) +(3 rows) + +--2. Only equivalence constraint can active the unique_btree_index rule. +explain (costs off) select * from unique_index_first where col2 = 2 and col4 < 4 and col3 = 3 and col1 = 1; + QUERY PLAN +---------------------------------------------------------- + Index Scan using index_no_unique on unique_index_first + Index Cond: ((col1 = 1) AND (col2 = 2) AND (col3 = 3)) + Filter: (col4 < 4) +(3 rows) + +--3. test seqscan +set enable_indexscan=off; +set enable_bitmapscan=off; +explain (costs off) select * from unique_index_first where col2 = 2 and col4 = 4 and col3 = 3 and col1 = 1; + QUERY PLAN +--------------------------------------------------------------------- + Seq Scan on unique_index_first + Filter: ((col2 = 2) AND (col4 = 4) AND (col3 = 3) AND (col1 = 1)) +(2 rows) + +explain (costs off) select * from unique_index_first where col2 = 2 and col4 < 4 and col3 = 3 and col1 = 1; + QUERY PLAN +--------------------------------------------------------------------- + Seq Scan on unique_index_first + Filter: ((col4 < 4) AND (col2 = 2) AND (col3 = 3) AND (col1 = 1)) +(2 rows) + +reset sql_beta_feature; +drop schema unique_index_first cascade; +NOTICE: drop cascades to table unique_index_first diff --git a/src/test/regress/expected/xc_dml.out b/src/test/regress/expected/xc_dml.out index 626f849fc..5bc733c4b 100644 --- a/src/test/regress/expected/xc_dml.out +++ b/src/test/regress/expected/xc_dml.out @@ -169,13 +169,14 @@ EXPLAIN (VERBOSE TRUE, COSTS FALSE)DELETE FROM DELETE_XC_C WHERE C2 IN (SELECT D (8 rows) EXPLAIN (VERBOSE TRUE, COSTS FALSE)DELETE FROM DELETE_XC_C WHERE C3 = 1 AND C1 = 5; - QUERY PLAN ------------------------------------------------------------------ + QUERY PLAN +--------------------------------------------------------------------- + [Bypass] Delete on public.delete_xc_c - -> Seq Scan on public.delete_xc_c + -> Index Scan using con_delete on public.delete_xc_c Output: ctid - Filter: ((delete_xc_c.c3 = 1) AND (delete_xc_c.c1 = 5)) -(4 rows) + Index Cond: ((delete_xc_c.c3 = 1) AND (delete_xc_c.c1 = 5)) +(5 rows) EXPLAIN (VERBOSE TRUE, COSTS FALSE)DELETE FROM DELETE_XC_C WHERE C3 = 0; QUERY PLAN @@ -231,13 +232,14 @@ EXPLAIN (VERBOSE TRUE, COSTS FALSE)DELETE FROM DELETE_XC_C WHERE C2 IN (SELECT D (8 rows) EXPLAIN (VERBOSE TRUE, COSTS FALSE)DELETE FROM DELETE_XC_C WHERE C3 = 1 AND C1 = 5; - QUERY PLAN ------------------------------------------------------------------ + QUERY PLAN +--------------------------------------------------------------------- + [Bypass] Delete on public.delete_xc_c - -> Seq Scan on public.delete_xc_c + -> Index Scan using con_delete on public.delete_xc_c Output: ctid - Filter: ((delete_xc_c.c3 = 1) AND (delete_xc_c.c1 = 5)) -(4 rows) + Index Cond: ((delete_xc_c.c3 = 1) AND (delete_xc_c.c1 = 5)) +(5 rows) EXPLAIN (VERBOSE TRUE, COSTS FALSE)DELETE FROM DELETE_XC_C WHERE C3 = 0; QUERY PLAN @@ -293,13 +295,14 @@ EXPLAIN (VERBOSE TRUE, COSTS FALSE)DELETE FROM DELETE_XC_C WHERE C2 IN (SELECT D (8 rows) EXPLAIN (VERBOSE TRUE, COSTS FALSE)DELETE FROM DELETE_XC_C WHERE C3 = 1 AND C1 = 5; - QUERY PLAN ------------------------------------------------------------------ + QUERY PLAN +--------------------------------------------------------------------- + [Bypass] Delete on public.delete_xc_c - -> Seq Scan on public.delete_xc_c + -> Index Scan using con_delete on public.delete_xc_c Output: ctid - Filter: ((delete_xc_c.c3 = 1) AND (delete_xc_c.c1 = 5)) -(4 rows) + Index Cond: ((delete_xc_c.c3 = 1) AND (delete_xc_c.c1 = 5)) +(5 rows) EXPLAIN (VERBOSE TRUE, COSTS FALSE)DELETE FROM DELETE_XC_C WHERE C3 = 0; QUERY PLAN @@ -532,12 +535,12 @@ EXPLAIN (VERBOSE TRUE, COSTS FALSE)DELETE FROM DELETE_XC_C WHERE C2 IN (SELECT D (8 rows) EXPLAIN (VERBOSE TRUE, COSTS FALSE)DELETE FROM DELETE_XC_C WHERE C3 = 1 AND C1 = 5; - QUERY PLAN ------------------------------------------------------------------ + QUERY PLAN +--------------------------------------------------------------------- Delete on public.delete_xc_c - -> Seq Scan on public.delete_xc_c + -> Index Scan using con_delete on public.delete_xc_c Output: ctid - Filter: ((delete_xc_c.c3 = 1) AND (delete_xc_c.c1 = 5)) + Index Cond: ((delete_xc_c.c3 = 1) AND (delete_xc_c.c1 = 5)) (4 rows) EXPLAIN (VERBOSE TRUE, COSTS FALSE)DELETE FROM DELETE_XC_C WHERE C3 = 0; @@ -845,12 +848,12 @@ EXPLAIN (VERBOSE TRUE, COSTS FALSE)DELETE FROM DELETE_XC_C WHERE C2 IN (SELECT D (8 rows) EXPLAIN (VERBOSE TRUE, COSTS FALSE)DELETE FROM DELETE_XC_C WHERE C3 = 1 AND C1 = 5; - QUERY PLAN ------------------------------------------------------------------ + QUERY PLAN +--------------------------------------------------------------------- Delete on public.delete_xc_c - -> Seq Scan on public.delete_xc_c + -> Index Scan using con_delete on public.delete_xc_c Output: ctid - Filter: ((delete_xc_c.c3 = 1) AND (delete_xc_c.c1 = 5)) + Index Cond: ((delete_xc_c.c3 = 1) AND (delete_xc_c.c1 = 5)) (4 rows) EXPLAIN (VERBOSE TRUE, COSTS FALSE)DELETE FROM DELETE_XC_C WHERE C3 = 0; @@ -914,13 +917,14 @@ EXPLAIN (VERBOSE TRUE, COSTS FALSE) UPDATE UPDATE_XC_C SET (C2) = (SELECT D3 FRO (7 rows) EXPLAIN (VERBOSE TRUE, COSTS FALSE) UPDATE UPDATE_XC_C SET C2 = 0 WHERE C3 = 1 AND C1 = 5; - QUERY PLAN ------------------------------------------------------------------ + QUERY PLAN +--------------------------------------------------------------------- + [Bypass] Update on public.update_xc_c - -> Seq Scan on public.update_xc_c + -> Index Scan using con_update on public.update_xc_c Output: c1, 0, c3, ctid - Filter: ((update_xc_c.c3 = 1) AND (update_xc_c.c1 = 5)) -(4 rows) + Index Cond: ((update_xc_c.c3 = 1) AND (update_xc_c.c1 = 5)) +(5 rows) EXPLAIN (VERBOSE TRUE, COSTS FALSE) UPDATE UPDATE_XC_C SET C2 = 1 WHERE C3 = 0; QUERY PLAN @@ -962,13 +966,14 @@ EXPLAIN (VERBOSE TRUE, COSTS FALSE) UPDATE UPDATE_XC_C SET (C2) = (SELECT D3 FRO (7 rows) EXPLAIN (VERBOSE TRUE, COSTS FALSE) UPDATE UPDATE_XC_C SET C2 = 0 WHERE C3 = 1 AND C1 = 5; - QUERY PLAN ------------------------------------------------------------------ + QUERY PLAN +--------------------------------------------------------------------- + [Bypass] Update on public.update_xc_c - -> Seq Scan on public.update_xc_c + -> Index Scan using con_update on public.update_xc_c Output: c1, 0, c3, ctid - Filter: ((update_xc_c.c3 = 1) AND (update_xc_c.c1 = 5)) -(4 rows) + Index Cond: ((update_xc_c.c3 = 1) AND (update_xc_c.c1 = 5)) +(5 rows) EXPLAIN (VERBOSE TRUE, COSTS FALSE) UPDATE UPDATE_XC_C SET C2 = 1 WHERE C3 = 0; QUERY PLAN @@ -1010,13 +1015,14 @@ EXPLAIN (VERBOSE TRUE, COSTS FALSE) UPDATE UPDATE_XC_C SET (C2) = (SELECT D3 FRO (7 rows) EXPLAIN (VERBOSE TRUE, COSTS FALSE) UPDATE UPDATE_XC_C SET C2 = 0 WHERE C3 = 1 AND C1 = 5; - QUERY PLAN ------------------------------------------------------------------ + QUERY PLAN +--------------------------------------------------------------------- + [Bypass] Update on public.update_xc_c - -> Seq Scan on public.update_xc_c + -> Index Scan using con_update on public.update_xc_c Output: c1, 0, c3, ctid - Filter: ((update_xc_c.c3 = 1) AND (update_xc_c.c1 = 5)) -(4 rows) + Index Cond: ((update_xc_c.c3 = 1) AND (update_xc_c.c1 = 5)) +(5 rows) EXPLAIN (VERBOSE TRUE, COSTS FALSE) UPDATE UPDATE_XC_C SET C2 = 1 WHERE C3 = 0; QUERY PLAN @@ -1291,12 +1297,12 @@ EXPLAIN (VERBOSE TRUE, COSTS FALSE) UPDATE UPDATE_XC_C SET (C2) = (SELECT D3 FRO (7 rows) EXPLAIN (VERBOSE TRUE, COSTS FALSE) UPDATE UPDATE_XC_C SET C2 = 0 WHERE C3 = 1 AND C1 = 5; - QUERY PLAN ------------------------------------------------------------------ + QUERY PLAN +--------------------------------------------------------------------- Update on public.update_xc_c - -> Seq Scan on public.update_xc_c + -> Index Scan using con_update on public.update_xc_c Output: c1, 0, c3, ctid - Filter: ((update_xc_c.c3 = 1) AND (update_xc_c.c1 = 5)) + Index Cond: ((update_xc_c.c3 = 1) AND (update_xc_c.c1 = 5)) (4 rows) EXPLAIN (VERBOSE TRUE, COSTS FALSE) UPDATE UPDATE_XC_C SET C2 = 1 WHERE C3 = 0; @@ -1349,12 +1355,12 @@ EXPLAIN (VERBOSE TRUE, COSTS FALSE) UPDATE UPDATE_XC_C SET (C2) = (SELECT D3 FRO (7 rows) EXPLAIN (VERBOSE TRUE, COSTS FALSE) UPDATE UPDATE_XC_C SET C2 = 0 WHERE C3 = 1 AND C1 = 5; - QUERY PLAN ------------------------------------------------------------------ + QUERY PLAN +--------------------------------------------------------------------- Update on public.update_xc_c - -> Seq Scan on public.update_xc_c + -> Index Scan using con_update on public.update_xc_c Output: c1, 0, c3, ctid - Filter: ((update_xc_c.c3 = 1) AND (update_xc_c.c1 = 5)) + Index Cond: ((update_xc_c.c3 = 1) AND (update_xc_c.c1 = 5)) (4 rows) EXPLAIN (VERBOSE TRUE, COSTS FALSE) UPDATE UPDATE_XC_C SET C2 = 1 WHERE C3 = 0; @@ -1630,12 +1636,12 @@ EXPLAIN (VERBOSE TRUE, COSTS FALSE) UPDATE UPDATE_XC_C SET (C2) = (SELECT D3 FRO (7 rows) EXPLAIN (VERBOSE TRUE, COSTS FALSE) UPDATE UPDATE_XC_C SET C2 = 0 WHERE C3 = 1 AND C1 = 5; - QUERY PLAN ------------------------------------------------------------------ + QUERY PLAN +--------------------------------------------------------------------- Update on public.update_xc_c - -> Seq Scan on public.update_xc_c + -> Index Scan using con_update on public.update_xc_c Output: c1, 0, c3, ctid - Filter: ((update_xc_c.c3 = 1) AND (update_xc_c.c1 = 5)) + Index Cond: ((update_xc_c.c3 = 1) AND (update_xc_c.c1 = 5)) (4 rows) EXPLAIN (VERBOSE TRUE, COSTS FALSE) UPDATE UPDATE_XC_C SET C2 = 1 WHERE C3 = 0; diff --git a/src/test/regress/parallel_schedule0 b/src/test/regress/parallel_schedule0 index dce0e132a..1243e5a86 100644 --- a/src/test/regress/parallel_schedule0 +++ b/src/test/regress/parallel_schedule0 @@ -760,3 +760,6 @@ test: leaky_function_operator #test: gs_guc test: smp +test: mulcolpk +test: pbe_partition +test: unique_index_first diff --git a/src/test/regress/sql/mulcolpk.sql b/src/test/regress/sql/mulcolpk.sql new file mode 100755 index 000000000..318cf4644 --- /dev/null +++ b/src/test/regress/sql/mulcolpk.sql @@ -0,0 +1,26 @@ +create schema mulcolpk; +set current_schema to mulcolpk; +create table mulcolpk (a int, b int); +insert into mulcolpk values (generate_series(0, 0), generate_series(1, 90)); +insert into mulcolpk values (generate_series(1, 10), generate_series(0, 0)); +analyze mulcolpk; + +--we just test row estimate in index path. +set enable_seqscan = off; +set enable_bitmapscan = off; +set plan_cache_mode = force_generic_plan; + +--1. create index +create index mulcolpk_idx on mulcolpk(a, b); +explain select * from mulcolpk where a = 0 and b = 0; + +--2. create unique index +drop index mulcolpk_idx; +create unique index mulcolpk_idx on mulcolpk(a, b); +explain select * from mulcolpk where a = 0 and b = 0; + +reset plan_cache_mode; +reset enable_bitmapscan; +reset enable_seqscan; + +drop schema mulcolpk cascade; diff --git a/src/test/regress/sql/pbe_partition.sql b/src/test/regress/sql/pbe_partition.sql new file mode 100755 index 000000000..06986a463 --- /dev/null +++ b/src/test/regress/sql/pbe_partition.sql @@ -0,0 +1,123 @@ +SET plan_cache_mode = force_generic_plan; + + +-- create range_partition table. + +CREATE TABLE partition_scan1(a int, b int) +PARTITION BY RANGE (a) +( + PARTITION P1 VALUES LESS THAN(10), + PARTITION P2 VALUES LESS THAN(20), + PARTITION P3 VALUES LESS THAN(30), + PARTITION P4 VALUES LESS THAN(40) +); + +CREATE UNIQUE INDEX index_on_partition_scan1 ON partition_scan1(a) LOCAL; + +insert into partition_scan1 values(generate_series(1,39,1), generate_series(1,39,1)); + +-- 等于param +prepare p1(int) as SELECT * FROM partition_scan1 s1 where s1.a = $1 ORDER BY s1.a; +explain (costs off) execute p1(10); +execute p1(10); + +--大于param +prepare p2(int) as SELECT * FROM partition_scan1 s1 where s1.a >$1 ORDER BY s1.a; +explain (costs off) execute p2(35); +execute p2(35); + +--小于param +prepare p3(int) as SELECT * FROM partition_scan1 s1 where s1.a <$1 ORDER BY s1.a; +explain (costs off) execute p3(35); +execute p3(35); + +-- 大于等于param +prepare p4(int) as SELECT * FROM partition_scan1 s1 where s1.a >=$1 ORDER BY s1.a; +explain (costs off) execute p4(35); +execute p4(35); + +-- 小于等于param +prepare p5(int) as SELECT * FROM partition_scan1 s1 where s1.a <=$1 ORDER BY s1.a; +explain (costs off) execute p5(35); +execute p5(35); + + +-- 等于expr +prepare p6(int,int) as SELECT * FROM partition_scan1 s1 where s1.a = $1+$2+1 ORDER BY s1.a; +explain (costs off) execute p6(10,10); +execute p6(10,10); + +--大于expr +prepare p7(int,int) as SELECT * FROM partition_scan1 s1 where s1.a > $1+$2+1 ORDER BY s1.a; +explain (costs off) execute p7(10,10); +execute p7(10,10); + +--小于expr +prepare p8(int,int) as SELECT * FROM partition_scan1 s1 where s1.a < $1+$2+1 ORDER BY s1.a; +explain (costs off) execute p8(10,10); +execute p8(10,10); + +-- 大于等于expr +prepare p9(int,int) as SELECT * FROM partition_scan1 s1 where s1.a >= $1+$2+1 ORDER BY s1.a; +explain (costs off) execute p9(10,10); +execute p9(10,10); + +-- 小于等于expr +prepare p10(int,int) as SELECT * FROM partition_scan1 s1 where s1.a <= $1+$2+1 ORDER BY s1.a; +explain (costs off) execute p10(10,10); +execute p10(10,10); + +--boolexpr_and +prepare p11(int,int) as SELECT * FROM partition_scan1 s1 where s1.a >= $1 and s1.b = $2 ORDER BY s1.a; +explain (costs off) execute p11(10,10); +execute p11(10,10); + +prepare p12(int,int) as SELECT * FROM partition_scan1 s1 where s1.a > $1 and s1.b < $2; +explain (costs off) execute p12(10,10); + +prepare p13(int,int) as SELECT * FROM partition_scan1 s1 where s1.a <= $1 and s1.b = $2+1; +explain (costs off) execute p13(10,10); + +prepare p131(int,int) as SELECT * FROM partition_scan1 s1 where s1.a < $1+1 and s1.b > $2+1; +explain (costs off) execute p131(10,10); + + +--update +prepare p14(int) as UPDATE partition_scan1 set b = b + 10 where a = $1; +explain (costs off) execute p14(10); + +prepare p15(int) as UPDATE partition_scan1 set b = b + 10 where a > $1; +explain (costs off) execute p15(10); + +prepare p16(int) as UPDATE partition_scan1 set b = b + 10 where a >= $1+1; +explain (costs off) execute p16(10); + +prepare p17(int) as UPDATE partition_scan1 set b = b + 10 where a <= $1; +explain (costs off) execute p17(10); + +prepare p18(int) as UPDATE partition_scan1 set b = b + 10 where a < $1; +explain (costs off) execute p18(10); + +prepare p181(int,int) as UPDATE partition_scan1 set b = b + 10 where a < $1 and b < $2; +explain (costs off) execute p181(10,10); + + + +-- delete +prepare p19(int) as DELETE FROM partition_scan1 where a=$1; +explain (costs off) execute p19(1); + +prepare p20(int) as DELETE FROM partition_scan1 where a>$1; +explain (costs off) execute p20(38); + +prepare p21(int) as DELETE FROM partition_scan1 where a<$1; +explain (costs off) execute p21(3); + +prepare p22(int) as DELETE FROM partition_scan1 where a>$1+1; +explain (costs off) execute p22(37); + +prepare p23(int) as DELETE FROM partition_scan1 where a<$1+1; +explain (costs off) execute p23(5); + +prepare p24(int,int) as DELETE FROM partition_scan1 where a>$1 and b>$2; +explain (costs off) execute p24(30,1); \ No newline at end of file diff --git a/src/test/regress/sql/unique_index_first.sql b/src/test/regress/sql/unique_index_first.sql new file mode 100755 index 000000000..1c7190326 --- /dev/null +++ b/src/test/regress/sql/unique_index_first.sql @@ -0,0 +1,22 @@ +create schema unique_index_first; +set current_schema to unique_index_first; + +create table unique_index_first (col1 int, col2 int, col3 int, col4 int, col5 int); +alter table unique_index_first add primary key (col1, col2, col3, col4); +create index index_no_unique on unique_index_first using btree(col1, col2, col3, col5); + +--1. A btree plan containing unique columns is prefered. +explain (costs off) select * from unique_index_first where col2 = 2 and col4 = 4 and col3 = 3 and col1 = 1; + +--2. Only equivalence constraint can active the unique_btree_index rule. +explain (costs off) select * from unique_index_first where col2 = 2 and col4 < 4 and col3 = 3 and col1 = 1; + +--3. test seqscan +set enable_indexscan=off; +set enable_bitmapscan=off; + +explain (costs off) select * from unique_index_first where col2 = 2 and col4 = 4 and col3 = 3 and col1 = 1; +explain (costs off) select * from unique_index_first where col2 = 2 and col4 < 4 and col3 = 3 and col1 = 1; + +reset sql_beta_feature; +drop schema unique_index_first cascade;