Skip to content

Commit

Permalink
planner: ban rule_generate_column_substitute when column's… (ping…
Browse files Browse the repository at this point in the history
  • Loading branch information
LittleFall authored Apr 17, 2020
1 parent 25580ea commit dd57c0a
Show file tree
Hide file tree
Showing 3 changed files with 283 additions and 1 deletion.
160 changes: 160 additions & 0 deletions cmd/explaintest/r/explain_generate_column_substitute.result
Original file line number Diff line number Diff line change
Expand Up @@ -324,3 +324,163 @@ Delete_4 N/A root N/A
└─IndexLookUp_11 10.00 root
├─IndexRangeScan_9(Build) 10.00 cop[tikv] table:t, index:expr_idx_e(_V$_expr_idx_e_0) range:[0,0], keep order:false, stats:pseudo
└─TableRowIDScan_10(Probe) 10.00 cop[tikv] table:t keep order:false, stats:pseudo
drop table if exists t;
create table t(c0 char as (c1), c1 int);
insert into t(c1) values (0), (1);
desc select * from t;
id estRows task access object operator info
TableReader_5 10000.00 root data:TableFullScan_4
└─TableFullScan_4 10000.00 cop[tikv] table:t keep order:false, stats:pseudo
select * from t;
c0 c1
0 0
1 1
drop table if exists t;
create table t(c0 int as (c1) unique, c1 int);
insert into t(c1) values (0), (1);
desc select * from t;
id estRows task access object operator info
Projection_3 10000.00 root test.t.c0, test.t.c0
└─IndexReader_7 10000.00 root index:IndexFullScan_6
└─IndexFullScan_6 10000.00 cop[tikv] table:t, index:c0(c0) keep order:false, stats:pseudo
select * from t;
c0 c1
0 0
1 1
drop table if exists t;
create table t(c0 char as (c1) unique, c1 int);
insert into t(c1) values (0), (1);
desc select * from t;
id estRows task access object operator info
TableReader_5 10000.00 root data:TableFullScan_4
└─TableFullScan_4 10000.00 cop[tikv] table:t keep order:false, stats:pseudo
select * from t;
c0 c1
0 0
1 1
drop table if exists t;
create table t(c0 int, c1 int as (c0+1) unique);
insert into t(c0) values (1),(2);
explain select c0+1 from t;
id estRows task access object operator info
IndexReader_7 10000.00 root index:IndexFullScan_6
└─IndexFullScan_6 10000.00 cop[tikv] table:t, index:c1(c1) keep order:false, stats:pseudo
select c0+1 from t;
c0+1
2
3
drop table if exists t;
create table t(c0 int, c1 int as (c0) unique);
insert into t(c0) values (1),(2);
explain select c1 from t where c1 = 2;
id estRows task access object operator info
Projection_4 1.00 root test.t.c1
└─Point_Get_5 1.00 root table:t, index:c1(c1)
select c1 from t where c1 = 2;
c1
2
drop table if exists t;
create table t(c0 double, c1 float as (c0+1) unique);
insert into t(c0) values (1.1),(2.2);
explain select c0+1 from t;
id estRows task access object operator info
Projection_3 10000.00 root plus(test.t.c0, 1)->Column#4
└─TableReader_5 10000.00 root data:TableFullScan_4
└─TableFullScan_4 10000.00 cop[tikv] table:t keep order:false, stats:pseudo
select c0+1 from t;
c0+1
2.1
3.2
drop table if exists t;
create table t(c0 float, c1 float as (c0+1) unique);
insert into t(c0) values (1.1),(2.2);
explain select c0+1 from t;
id estRows task access object operator info
Projection_3 10000.00 root plus(test.t.c0, 1)->Column#4
└─TableReader_5 10000.00 root data:TableFullScan_4
└─TableFullScan_4 10000.00 cop[tikv] table:t keep order:false, stats:pseudo
select c0+1 from t;
c0+1
2.100000023841858
3.200000047683716
drop table if exists t;
create table t(c0 int, c1 tinyint as (c0) unique);
insert into t(c0) values (1),(127);
desc select * from t;
id estRows task access object operator info
Projection_3 10000.00 root test.t.c1, test.t.c1
└─IndexReader_7 10000.00 root index:IndexFullScan_6
└─IndexFullScan_6 10000.00 cop[tikv] table:t, index:c1(c1) keep order:false, stats:pseudo
select * from t;
c0 c1
1 1
127 127
drop table if exists t;
create table t(c0 int, c1 year as (c0) unique);
insert into t(c0) values (48);
desc select * from t;
id estRows task access object operator info
TableReader_5 10000.00 root data:TableFullScan_4
└─TableFullScan_4 10000.00 cop[tikv] table:t keep order:false, stats:pseudo
select * from t;
c0 c1
48 2048
drop table t;
create table t(c0 varchar(10), c1 char(10) as (c0) unique);
insert into t(c0) values ("a ");
desc select * from t;
id estRows task access object operator info
TableReader_5 10000.00 root data:TableFullScan_4
└─TableFullScan_4 10000.00 cop[tikv] table:t keep order:false, stats:pseudo
select * from t;
c0 c1
a a
drop table if exists t;
create table t(c0 timestamp, c1 date as (c0) unique);
insert into t(c0) values('2038-01-19 03:14:07.999999');
desc select * from t;
id estRows task access object operator info
TableReader_5 10000.00 root data:TableFullScan_4
└─TableFullScan_4 10000.00 cop[tikv] table:t keep order:false, stats:pseudo
select * from t;
c0 c1
2038-01-19 03:14:08 2038-01-19
drop table t;
create table t(c0 decimal(5,3), c1 decimal(5,2) as (c0) unique);
insert into t(c0) values (3.1415926);
desc select * from t;
id estRows task access object operator info
TableReader_5 10000.00 root data:TableFullScan_4
└─TableFullScan_4 10000.00 cop[tikv] table:t keep order:false, stats:pseudo
select * from t;
c0 c1
3.142 3.14
drop table t;
create table t(c0 char(10), c1 binary(10) as (c0) unique);
select hex(c0) from (select c0 from t use index()) tt;
hex(c0)
drop table t;
create table t(c0 char(10), c1 binary(10) as (c0) unique);
select c0 from t use index();
c0
desc select c0 from t use index();
id estRows task access object operator info
TableReader_5 10000.00 root data:TableFullScan_4
└─TableFullScan_4 10000.00 cop[tikv] table:t keep order:false, stats:pseudo
select hex(c0) from (select c0 from t use index()) tt;
hex(c0)
desc select hex(c0) from (select c0 from t use index()) tt;
id estRows task access object operator info
Projection_4 10000.00 root hex(test.t.c0)->Column#4
└─TableReader_6 10000.00 root data:TableFullScan_5
└─TableFullScan_5 10000.00 cop[tikv] table:t keep order:false, stats:pseudo
drop table t;
create table t(a enum('1', '2', '3'), b enum('a', 'b', 'c') as (a) unique);
insert into t(a) values ('1');
desc select * from t;
id estRows task access object operator info
TableReader_5 10000.00 root data:TableFullScan_4
└─TableFullScan_4 10000.00 cop[tikv] table:t keep order:false, stats:pseudo
select * from t;
a b
1 a
90 changes: 90 additions & 0 deletions cmd/explaintest/t/explain_generate_column_substitute.test
Original file line number Diff line number Diff line change
Expand Up @@ -132,3 +132,93 @@ desc update t set a=2, b = 3 where b+a = 3;
# test delete
desc delete from t where a+1 = 3;
desc delete from t where b+a = 0;

# test different column type
drop table if exists t;
create table t(c0 char as (c1), c1 int);
insert into t(c1) values (0), (1);
desc select * from t;
select * from t;

drop table if exists t;
create table t(c0 int as (c1) unique, c1 int);
insert into t(c1) values (0), (1);
desc select * from t;
select * from t;

drop table if exists t;
create table t(c0 char as (c1) unique, c1 int);
insert into t(c1) values (0), (1);
desc select * from t;
select * from t;

drop table if exists t;
create table t(c0 int, c1 int as (c0+1) unique);
insert into t(c0) values (1),(2);
explain select c0+1 from t;
select c0+1 from t;

drop table if exists t;
create table t(c0 int, c1 int as (c0) unique);
insert into t(c0) values (1),(2);
explain select c1 from t where c1 = 2;
select c1 from t where c1 = 2;

drop table if exists t;
create table t(c0 double, c1 float as (c0+1) unique);
insert into t(c0) values (1.1),(2.2);
explain select c0+1 from t;
select c0+1 from t;

drop table if exists t;
create table t(c0 float, c1 float as (c0+1) unique);
insert into t(c0) values (1.1),(2.2);
explain select c0+1 from t;
select c0+1 from t;

drop table if exists t;
create table t(c0 int, c1 tinyint as (c0) unique);
insert into t(c0) values (1),(127);
desc select * from t;
select * from t;

drop table if exists t;
create table t(c0 int, c1 year as (c0) unique);
insert into t(c0) values (48);
desc select * from t;
select * from t;

drop table t;
create table t(c0 varchar(10), c1 char(10) as (c0) unique);
insert into t(c0) values ("a ");
desc select * from t;
select * from t;

drop table if exists t;
create table t(c0 timestamp, c1 date as (c0) unique);
insert into t(c0) values('2038-01-19 03:14:07.999999');
desc select * from t;
select * from t;

drop table t;
create table t(c0 decimal(5,3), c1 decimal(5,2) as (c0) unique);
insert into t(c0) values (3.1415926);
desc select * from t;
select * from t;

drop table t;
create table t(c0 char(10), c1 binary(10) as (c0) unique);
select hex(c0) from (select c0 from t use index()) tt;

drop table t;
create table t(c0 char(10), c1 binary(10) as (c0) unique);
select c0 from t use index();
desc select c0 from t use index();
select hex(c0) from (select c0 from t use index()) tt;
desc select hex(c0) from (select c0 from t use index()) tt;

drop table t;
create table t(a enum('1', '2', '3'), b enum('a', 'b', 'c') as (a) unique);
insert into t(a) values ('1');
desc select * from t;
select * from t;
34 changes: 33 additions & 1 deletion planner/core/rule_generate_column_substitute.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
"context"

"github.com/pingcap/parser/ast"
"github.com/pingcap/parser/mysql"
"github.com/pingcap/tidb/expression"
"github.com/pingcap/tidb/sessionctx"
"github.com/pingcap/tidb/types"
Expand Down Expand Up @@ -44,6 +45,37 @@ func (gc *gcSubstituter) optimize(ctx context.Context, lp LogicalPlan) (LogicalP
return gc.substitute(ctx, lp, exprToColumn), nil
}

// Only integers can be substituted between different types now.
// Because they are all stored as 64-bit integers in chunks.
// And the inserted value will meet the requirements of virtual column type and virtual column expression type.
func getSubstitutableType(colType *types.FieldType) int {
switch colType.Tp {
case mysql.TypeTiny, mysql.TypeShort, mysql.TypeInt24, mysql.TypeLong, mysql.TypeLonglong:
return -1
}
return int(colType.Tp)
}

// We do not check whether flen is the same.
// Because different flen will not cause data truncation.
// See https://github.com/pingcap/tidb/pull/16316.
func checkSubstitutability(c1, c2 *types.FieldType) bool {
partialEqual := c1.Decimal == c2.Decimal &&
c1.Charset == c2.Charset &&
c1.Collate == c2.Collate &&
getSubstitutableType(c1) == getSubstitutableType(c2) &&
mysql.HasUnsignedFlag(c1.Flag) == mysql.HasUnsignedFlag(c2.Flag)
if !partialEqual || len(c1.Elems) != len(c2.Elems) {
return false
}
for i := range c1.Elems {
if c1.Elems[i] != c2.Elems[i] {
return false
}
}
return true
}

// collectGenerateColumn collect the generate column and save them to a map from their expressions to themselves.
// For the sake of simplicity, we don't collect the stored generate column because we can't get their expressions directly.
// TODO: support stored generate column.
Expand All @@ -62,7 +94,7 @@ func collectGenerateColumn(lp LogicalPlan, exprToColumn ExprColumnMap) {
if colInfo.IsGenerated() && !colInfo.GeneratedStored {
s := ds.schema.Columns
col := expression.ColInfo2Col(s, colInfo)
if col != nil {
if col != nil && checkSubstitutability(col.GetType(), col.VirtualExpr.GetType()) {
exprToColumn[col.VirtualExpr] = col
}
}
Expand Down

0 comments on commit dd57c0a

Please sign in to comment.