回合路径生唯一索引优先,多列主键选择率估算错误,无法禁止索引扫描,修正索引代价模型

This commit is contained in:
shirley_zhengx 2021-12-22 10:36:50 +08:00
parent ee30a37bb3
commit 424b1c0b79
17 changed files with 1211 additions and 55 deletions

View File

@ -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|

View File

@ -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}
};

View File

@ -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",

View File

@ -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;

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;

View File

@ -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;

View File

@ -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) \

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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;

View File

@ -760,3 +760,6 @@ test: leaky_function_operator
#test: gs_guc
test: smp
test: mulcolpk
test: pbe_partition
test: unique_index_first

View File

@ -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;

View File

@ -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);

View File

@ -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;