Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

planner, expression: take NullFlag into consideration when optimize the int non-const <cmp > non-int const #20040

Closed
Closed
2 changes: 1 addition & 1 deletion cmd/explaintest/r/explain_easy.result
Original file line number Diff line number Diff line change
Expand Up @@ -407,7 +407,7 @@ Projection_3 10000.00 root ne(test.t.a, 123455)->Column#3
└─TableFullScan_4 10000.00 cop[tikv] table:t keep order:false, stats:pseudo
explain select t.a = 12345678912345678998789678687678.111 from t;
id estRows task access object operator info
Projection_3 10000.00 root 0->Column#3
Projection_3 10000.00 root eq(test.t.a, 4294967295)->Column#3
└─TableReader_5 10000.00 root data:TableFullScan_4
└─TableFullScan_4 10000.00 cop[tikv] table:t keep order:false, stats:pseudo
drop table if exists t;
Expand Down
4 changes: 4 additions & 0 deletions expression/builtin_compare.go
Original file line number Diff line number Diff line change
Expand Up @@ -1298,6 +1298,8 @@ func (c *compareFunctionClass) refineArgs(ctx sessionctx.Context, args []Express
if arg0IsInt && !arg0IsCon && !arg1IsInt && arg1IsCon {
arg1, isExceptional = RefineComparedConstant(ctx, *arg0Type, arg1, c.op)
finalArg1 = arg1
// If the arg has not null flag, it is not an exception
isExceptional = isExceptional && mysql.HasNotNullFlag(arg0Type.Flag)
if isExceptional && arg1.GetType().EvalType() == types.ETInt {
// Judge it is inf or -inf
// For int:
Expand All @@ -1317,6 +1319,8 @@ func (c *compareFunctionClass) refineArgs(ctx sessionctx.Context, args []Express
if arg1IsInt && !arg1IsCon && !arg0IsInt && arg0IsCon {
arg0, isExceptional = RefineComparedConstant(ctx, *arg1Type, arg0, symmetricOp[c.op])
finalArg0 = arg0
// If the arg has not null flag, it is not an exception
isExceptional = isExceptional && mysql.HasNotNullFlag(arg1Type.Flag)
if isExceptional && arg0.GetType().EvalType() == types.ETInt {
if arg0.Value.GetInt64()&1 == 1 {
isNegativeInfinite = true
Expand Down
20 changes: 19 additions & 1 deletion expression/builtin_compare_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import (
)

func (s *testEvaluatorSuite) TestCompareFunctionWithRefine(c *C) {
tblInfo := newTestTableBuilder("").add("a", mysql.TypeLong).build()
tblInfo := newTestTableBuilder("").add("a", mysql.TypeLong, mysql.NotNullFlag).build()
tests := []struct {
exprStr string
result string
Expand Down Expand Up @@ -78,6 +78,24 @@ func (s *testEvaluatorSuite) TestCompareFunctionWithRefine(c *C) {
}
}

func (s *testEvaluatorSuite) TestCompareFunctionWithoutRefine(c *C) {
tblInfo := newTestTableBuilder("t0").add("c0", mysql.TypeLong, 0).build()
tests := []struct {
exprStr string
result string
}{
{"1.5071004017670217e-01=t0.c0", "eq(0.15071004017670217, cast(t0.c0, double BINARY))"},
}
cols, names, err := ColumnInfos2ColumnsAndNames(s.ctx, model.NewCIStr(""), tblInfo.Name, tblInfo.Cols(), tblInfo)
c.Assert(err, IsNil)
schema := NewSchema(cols...)
for _, t := range tests {
f, err := ParseSimpleExprsWithNames(s.ctx, t.exprStr, schema, names)
c.Assert(err, IsNil)
c.Assert(f[0].String(), Equals, t.result)
}
}

func (s *testEvaluatorSuite) TestCompare(c *C) {
intVal, uintVal, realVal, stringVal, decimalVal := 1, uint64(1), 1.1, "123", types.NewDecFromFloatForTest(123.123)
timeVal := types.NewTime(types.FromGoTime(time.Now()), mysql.TypeDatetime, 6)
Expand Down
7 changes: 5 additions & 2 deletions expression/expression_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ func (s *testEvaluatorSuite) TestNewValuesFunc(c *C) {
}

func (s *testEvaluatorSuite) TestEvaluateExprWithNull(c *C) {
tblInfo := newTestTableBuilder("").add("col0", mysql.TypeLonglong).add("col1", mysql.TypeLonglong).build()
tblInfo := newTestTableBuilder("").add("col0", mysql.TypeLonglong, 0).add("col1", mysql.TypeLonglong, 0).build()
schema := tableInfoToSchemaForTest(tblInfo)
col0 := schema.Columns[0]
col1 := schema.Columns[1]
Expand Down Expand Up @@ -142,15 +142,17 @@ type testTableBuilder struct {
tableName string
columnNames []string
tps []byte
flags []uint
}

func newTestTableBuilder(tableName string) *testTableBuilder {
return &testTableBuilder{tableName: tableName}
}

func (builder *testTableBuilder) add(name string, tp byte) *testTableBuilder {
func (builder *testTableBuilder) add(name string, tp byte, flag uint) *testTableBuilder {
builder.columnNames = append(builder.columnNames, name)
builder.tps = append(builder.tps, tp)
builder.flags = append(builder.flags, flag)
return builder
}

Expand All @@ -165,6 +167,7 @@ func (builder *testTableBuilder) build() *model.TableInfo {
fieldType := types.NewFieldType(tp)
fieldType.Flen, fieldType.Decimal = mysql.GetDefaultFieldLengthAndDecimal(tp)
fieldType.Charset, fieldType.Collate = types.DefaultCharsetForType(tp)
fieldType.Flag = builder.flags[i]
ti.Columns = append(ti.Columns, &model.ColumnInfo{
ID: int64(i + 1),
Name: model.NewCIStr(colName),
Expand Down
2 changes: 1 addition & 1 deletion planner/core/cbo_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -451,7 +451,7 @@ func (s *testAnalyzeSuite) TestPreparedNullParam(c *C) {
testKit := testkit.NewTestKit(c, store)
testKit.MustExec("use test")
testKit.MustExec("drop table if exists t")
testKit.MustExec("create table t (id int, KEY id (id))")
testKit.MustExec("create table t (id int not null, KEY id (id))")
testKit.MustExec("insert into t values (1), (2), (3)")

sql := "select * from t where id = ?"
Expand Down
18 changes: 18 additions & 0 deletions planner/core/expression_rewriter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -298,3 +298,21 @@ func (s *testExpressionRewriterSuite) TestIssue20007(c *C) {
testkit.Rows("2 epic wiles 2020-01-02 23:29:51", "3 silly burnell 2020-02-25 07:43:07"))
}
}

// TestIssue16679 contains tests for https://github.com/pingcap/tidb/issues/16679
func (s *testExpressionRewriterSuite) TestIssue16679(c *C) {
defer testleak.AfterTest(c)()
store, dom, err := newStoreWithBootstrap()
c.Assert(err, IsNil)
tk := testkit.NewTestKit(c, store)
defer func() {
dom.Close()
store.Close()
}()

tk.MustExec("use test")
tk.MustExec(`drop table if exists t0;`)
tk.MustExec(`create table t0(c0 int);`)
tk.MustExec(`insert into t0 values(null);`)
tk.MustQuery("select * from t0 where ((!(1.5071004017670217e-01=t0.c0))) IS NULL").Check(testkit.Rows("<nil>"))
}
23 changes: 23 additions & 0 deletions planner/core/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -697,6 +697,29 @@ func (s *testIntegrationSuite) TestPartitionTableStats(c *C) {
func (s *testIntegrationSuite) TestPartitionPruningForInExpr(c *C) {
tk := testkit.NewTestKit(c, s.store)

tk.MustExec("use test")
tk.MustExec("drop table if exists t")
tk.MustExec("create table t(a int(11) not null, b int) partition by range (a) (partition p0 values less than (4), partition p1 values less than(10), partition p2 values less than maxvalue);")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we add a new test for the column a without not null?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have added a new test for it.

tk.MustExec("insert into t values (1, 1),(10, 10),(11, 11)")

var input []string
var output []struct {
SQL string
Plan []string
}
s.testData.GetTestCases(c, &input, &output)
for i, tt := range input {
s.testData.OnRecord(func() {
output[i].SQL = tt
output[i].Plan = s.testData.ConvertRowsToStrings(tk.MustQuery(tt).Rows())
})
tk.MustQuery(tt).Check(testkit.Rows(output[i].Plan...))
}
}

func (s *testIntegrationSuite) TestPartitionPruningForInExprNullParam(c *C) {
tk := testkit.NewTestKit(c, s.store)

tk.MustExec("use test")
tk.MustExec("drop table if exists t")
tk.MustExec("create table t(a int(11), b int) partition by range (a) (partition p0 values less than (4), partition p1 values less than(10), partition p2 values less than maxvalue);")
Expand Down
6 changes: 6 additions & 0 deletions planner/core/testdata/integration_suite_in.json
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,12 @@
"explain select * from t where b in (3, 4)"
]
},
{
"name": "TestPartitionPruningForInExprNullParam",
"cases": [
"explain select * from t where a in (15, 0.12, 3.47)"
]
},
{
"name": "TestStreamAggProp",
"cases": [
Expand Down
13 changes: 13 additions & 0 deletions planner/core/testdata/integration_suite_out.json
Original file line number Diff line number Diff line change
Expand Up @@ -611,6 +611,19 @@
}
]
},
{
"Name": "TestPartitionPruningForInExprNullParam",
"Cases": [
{
"SQL": "explain select * from t where a in (15, 0.12, 3.47)",
"Plan": [
"TableReader_7 9600.40 root partition:all data:Selection_6",
"└─Selection_6 9600.40 cop[tikv] or(eq(test.t.a, 15), or(eq(cast(test.t.a), 0.12), eq(cast(test.t.a), 3.47)))",
" └─TableFullScan_5 10000.00 cop[tikv] table:t keep order:false, stats:pseudo"
]
}
]
},
{
"Name": "TestStreamAggProp",
"Cases": [
Expand Down