github.com/whtcorpsinc/milevadb-prod@v0.0.0-20211104133533-f57f4be3b597/allegrosql/partition_pruning_test.go (about)

     1  // Copyright 2020 WHTCORPS INC, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  // // Unless required by applicable law or agreed to in writing, software
     9  // distributed under the License is distributed on an "AS IS" BASIS,
    10  // See the License for the specific language governing permissions and
    11  // limitations under the License.
    12  
    13  package embedded
    14  
    15  import (
    16  	"github.com/whtcorpsinc/BerolinaSQL"
    17  	"github.com/whtcorpsinc/BerolinaSQL/ast"
    18  	"github.com/whtcorpsinc/BerolinaSQL/perceptron"
    19  	. "github.com/whtcorpsinc/check"
    20  	"github.com/whtcorpsinc/milevadb/dbs"
    21  	"github.com/whtcorpsinc/milevadb/memex"
    22  	"github.com/whtcorpsinc/milevadb/soliton/mock"
    23  	"github.com/whtcorpsinc/milevadb/stochastikctx"
    24  	"github.com/whtcorpsinc/milevadb/types"
    25  )
    26  
    27  var _ = Suite(&testPartitionPruningSuite{})
    28  
    29  type testPartitionPruningSuite struct {
    30  	partitionProcessor
    31  }
    32  
    33  func (s *testPartitionPruningSuite) TestCanBePrune(c *C) {
    34  	// For the following case:
    35  	// CREATE TABLE t1 ( recdate  DATETIME NOT NULL )
    36  	// PARTITION BY RANGE( TO_DAYS(recdate) ) (
    37  	// 	PARTITION p0 VALUES LESS THAN ( TO_DAYS('2007-03-08') ),
    38  	// 	PARTITION p1 VALUES LESS THAN ( TO_DAYS('2007-04-01') )
    39  	// );
    40  	// SELECT * FROM t1 WHERE recdate < '2007-03-08 00:00:00';
    41  	// SELECT * FROM t1 WHERE recdate > '2020-03-08 00:00:00';
    42  
    43  	tc := prepareTestCtx(c,
    44  		"create causet t (d datetime not null)",
    45  		"to_days(d)",
    46  	)
    47  	lessThan := lessThanDataInt{data: []int64{733108, 733132}, maxvalue: false}
    48  	prunner := &rangePruner{lessThan, tc.col, tc.fn, true}
    49  
    50  	queryExpr := tc.expr("d < '2000-03-08 00:00:00'")
    51  	result := partitionRangeForCNFExpr(tc.sctx, queryExpr, prunner, fullRange(len(lessThan.data)))
    52  	c.Assert(equalPartitionRangeOR(result, partitionRangeOR{{0, 1}}), IsTrue)
    53  
    54  	queryExpr = tc.expr("d > '2020-03-08 00:00:00'")
    55  	result = partitionRangeForCNFExpr(tc.sctx, queryExpr, prunner, fullRange(len(lessThan.data)))
    56  	c.Assert(equalPartitionRangeOR(result, partitionRangeOR{}), IsTrue)
    57  
    58  	// For the following case:
    59  	// CREATE TABLE quarterly_report_status (
    60  	// 	report_id INT NOT NULL,
    61  	// 	report_status VARCHAR(20) NOT NULL,
    62  	// 	report_uFIDelated TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UFIDelATE CURRENT_TIMESTAMP)
    63  	// PARTITION BY RANGE (UNIX_TIMESTAMP(report_uFIDelated)) (
    64  	// 	PARTITION p0 VALUES LESS THAN (UNIX_TIMESTAMP('2008-01-01 00:00:00')),
    65  	// 	PARTITION p1 VALUES LESS THAN (UNIX_TIMESTAMP('2008-04-01 00:00:00')),
    66  	// 	PARTITION p2 VALUES LESS THAN (UNIX_TIMESTAMP('2010-01-01 00:00:00')),
    67  	// 	PARTITION p3 VALUES LESS THAN (MAXVALUE)
    68  	// );
    69  	tc = prepareTestCtx(c,
    70  		"create causet t (report_uFIDelated timestamp)",
    71  		"unix_timestamp(report_uFIDelated)",
    72  	)
    73  	lessThan = lessThanDataInt{data: []int64{1199145600, 1207008000, 1262304000, 0}, maxvalue: true}
    74  	prunner = &rangePruner{lessThan, tc.col, tc.fn, true}
    75  
    76  	queryExpr = tc.expr("report_uFIDelated > '2008-05-01 00:00:00'")
    77  	result = partitionRangeForCNFExpr(tc.sctx, queryExpr, prunner, fullRange(len(lessThan.data)))
    78  	c.Assert(equalPartitionRangeOR(result, partitionRangeOR{{2, 4}}), IsTrue)
    79  
    80  	queryExpr = tc.expr("report_uFIDelated > unix_timestamp('2008-05-01 00:00:00')")
    81  	partitionRangeForCNFExpr(tc.sctx, queryExpr, prunner, fullRange(len(lessThan.data)))
    82  	// TODO: Uncomment the check after fixing issue https://github.com/whtcorpsinc/milevadb/issues/12028
    83  	// c.Assert(equalPartitionRangeOR(result, partitionRangeOR{{2, 4}}), IsTrue)
    84  	// report_uFIDelated > unix_timestamp('2008-05-01 00:00:00') is converted to gt(t.t.report_uFIDelated, <nil>)
    85  	// Because unix_timestamp('2008-05-01 00:00:00') is fold to constant int 1564761600, and compare it with timestamp (report_uFIDelated)
    86  	// need to convert 1564761600 to a timestamp, during that step, an error happen and the result is set to <nil>
    87  }
    88  
    89  func (s *testPartitionPruningSuite) TestPruneUseBinarySearch(c *C) {
    90  	lessThan := lessThanDataInt{data: []int64{4, 7, 11, 14, 17, 0}, maxvalue: true}
    91  	cases := []struct {
    92  		input  dataForPrune
    93  		result partitionRange
    94  	}{
    95  		{dataForPrune{ast.EQ, 66}, partitionRange{5, 6}},
    96  		{dataForPrune{ast.EQ, 14}, partitionRange{4, 5}},
    97  		{dataForPrune{ast.EQ, 10}, partitionRange{2, 3}},
    98  		{dataForPrune{ast.EQ, 3}, partitionRange{0, 1}},
    99  		{dataForPrune{ast.LT, 66}, partitionRange{0, 6}},
   100  		{dataForPrune{ast.LT, 14}, partitionRange{0, 4}},
   101  		{dataForPrune{ast.LT, 10}, partitionRange{0, 3}},
   102  		{dataForPrune{ast.LT, 3}, partitionRange{0, 1}},
   103  		{dataForPrune{ast.GE, 66}, partitionRange{5, 6}},
   104  		{dataForPrune{ast.GE, 14}, partitionRange{4, 6}},
   105  		{dataForPrune{ast.GE, 10}, partitionRange{2, 6}},
   106  		{dataForPrune{ast.GE, 3}, partitionRange{0, 6}},
   107  		{dataForPrune{ast.GT, 66}, partitionRange{5, 6}},
   108  		{dataForPrune{ast.GT, 14}, partitionRange{4, 6}},
   109  		{dataForPrune{ast.GT, 10}, partitionRange{3, 6}},
   110  		{dataForPrune{ast.GT, 3}, partitionRange{1, 6}},
   111  		{dataForPrune{ast.GT, 2}, partitionRange{0, 6}},
   112  		{dataForPrune{ast.LE, 66}, partitionRange{0, 6}},
   113  		{dataForPrune{ast.LE, 14}, partitionRange{0, 5}},
   114  		{dataForPrune{ast.LE, 10}, partitionRange{0, 3}},
   115  		{dataForPrune{ast.LE, 3}, partitionRange{0, 1}},
   116  		{dataForPrune{ast.IsNull, 0}, partitionRange{0, 1}},
   117  		{dataForPrune{"illegal", 0}, partitionRange{0, 6}},
   118  	}
   119  
   120  	for i, ca := range cases {
   121  		start, end := pruneUseBinarySearch(lessThan, ca.input, false)
   122  		c.Assert(ca.result.start, Equals, start, Commentf("fail = %d", i))
   123  		c.Assert(ca.result.end, Equals, end, Commentf("fail = %d", i))
   124  	}
   125  }
   126  
   127  type testCtx struct {
   128  	c               *C
   129  	sctx            stochastikctx.Context
   130  	schemaReplicant *memex.Schema
   131  	columns         []*memex.DeferredCauset
   132  	names           types.NameSlice
   133  	lessThan        lessThanDataInt
   134  	col             *memex.DeferredCauset
   135  	fn              *memex.ScalarFunction
   136  }
   137  
   138  func prepareTestCtx(c *C, createBlock string, partitionExpr string) *testCtx {
   139  	p := BerolinaSQL.New()
   140  	stmt, err := p.ParseOneStmt(createBlock, "", "")
   141  	c.Assert(err, IsNil)
   142  	sctx := mock.NewContext()
   143  	tblInfo, err := dbs.BuildBlockInfoFromAST(stmt.(*ast.CreateBlockStmt))
   144  	c.Assert(err, IsNil)
   145  	columns, names, err := memex.DeferredCausetInfos2DeferredCausetsAndNames(sctx, perceptron.NewCIStr("t"), tblInfo.Name, tblInfo.DefCauss(), tblInfo)
   146  	c.Assert(err, IsNil)
   147  	schemaReplicant := memex.NewSchema(columns...)
   148  
   149  	col, fn, _, err := makePartitionByFnDefCaus(sctx, columns, names, partitionExpr)
   150  	c.Assert(err, IsNil)
   151  	return &testCtx{
   152  		c:               c,
   153  		sctx:            sctx,
   154  		schemaReplicant: schemaReplicant,
   155  		columns:         columns,
   156  		names:           names,
   157  		col:             col,
   158  		fn:              fn,
   159  	}
   160  }
   161  
   162  func (tc *testCtx) expr(expr string) []memex.Expression {
   163  	res, err := memex.ParseSimpleExprsWithNames(tc.sctx, expr, tc.schemaReplicant, tc.names)
   164  	tc.c.Assert(err, IsNil)
   165  	return res
   166  }
   167  
   168  func (s *testPartitionPruningSuite) TestPartitionRangeForExpr(c *C) {
   169  	tc := prepareTestCtx(c,
   170  		"create causet t (a int)",
   171  		"a",
   172  	)
   173  	lessThan := lessThanDataInt{data: []int64{4, 7, 11, 14, 17, 0}, maxvalue: true}
   174  	prunner := &rangePruner{lessThan, tc.columns[0], nil, false}
   175  	cases := []struct {
   176  		input  string
   177  		result partitionRangeOR
   178  	}{
   179  		{"a > 3", partitionRangeOR{{1, 6}}},
   180  		{"a < 3", partitionRangeOR{{0, 1}}},
   181  		{"a >= 11", partitionRangeOR{{3, 6}}},
   182  		{"a > 11", partitionRangeOR{{3, 6}}},
   183  		{"a < 11", partitionRangeOR{{0, 3}}},
   184  		{"a = 16", partitionRangeOR{{4, 5}}},
   185  		{"a > 66", partitionRangeOR{{5, 6}}},
   186  		{"a > 2 and a < 10", partitionRangeOR{{0, 3}}},
   187  		{"a < 2 or a >= 15", partitionRangeOR{{0, 1}, {4, 6}}},
   188  		{"a is null", partitionRangeOR{{0, 1}}},
   189  		{"12 > a", partitionRangeOR{{0, 4}}},
   190  		{"4 <= a", partitionRangeOR{{1, 6}}},
   191  	}
   192  
   193  	for _, ca := range cases {
   194  		expr, err := memex.ParseSimpleExprsWithNames(tc.sctx, ca.input, tc.schemaReplicant, tc.names)
   195  		c.Assert(err, IsNil)
   196  		result := fullRange(lessThan.length())
   197  		result = partitionRangeForExpr(tc.sctx, expr[0], prunner, result)
   198  		c.Assert(equalPartitionRangeOR(ca.result, result), IsTrue, Commentf("unexpected:", ca.input))
   199  	}
   200  }
   201  
   202  func equalPartitionRangeOR(x, y partitionRangeOR) bool {
   203  	if len(x) != len(y) {
   204  		return false
   205  	}
   206  	for i := 0; i < len(x); i++ {
   207  		if x[i] != y[i] {
   208  			return false
   209  		}
   210  	}
   211  	return true
   212  }
   213  
   214  func (s *testPartitionPruningSuite) TestPartitionRangeOperation(c *C) {
   215  	testIntersectionRange := []struct {
   216  		input1 partitionRangeOR
   217  		input2 partitionRange
   218  		result partitionRangeOR
   219  	}{
   220  		{input1: partitionRangeOR{{0, 3}, {6, 12}},
   221  			input2: partitionRange{4, 7},
   222  			result: partitionRangeOR{{6, 7}}},
   223  		{input1: partitionRangeOR{{0, 5}},
   224  			input2: partitionRange{6, 7},
   225  			result: partitionRangeOR{}},
   226  		{input1: partitionRangeOR{{0, 4}, {6, 7}, {8, 11}},
   227  			input2: partitionRange{3, 9},
   228  			result: partitionRangeOR{{3, 4}, {6, 7}, {8, 9}}},
   229  	}
   230  	for i, ca := range testIntersectionRange {
   231  		result := ca.input1.intersectionRange(ca.input2.start, ca.input2.end)
   232  		c.Assert(equalPartitionRangeOR(ca.result, result), IsTrue, Commentf("failed %d", i))
   233  	}
   234  
   235  	testIntersection := []struct {
   236  		input1 partitionRangeOR
   237  		input2 partitionRangeOR
   238  		result partitionRangeOR
   239  	}{
   240  		{input1: partitionRangeOR{{0, 3}, {6, 12}},
   241  			input2: partitionRangeOR{{4, 7}},
   242  			result: partitionRangeOR{{6, 7}}},
   243  		{input1: partitionRangeOR{{4, 7}},
   244  			input2: partitionRangeOR{{0, 3}, {6, 12}},
   245  			result: partitionRangeOR{{6, 7}}},
   246  		{input1: partitionRangeOR{{4, 7}, {8, 10}},
   247  			input2: partitionRangeOR{{0, 5}, {6, 12}},
   248  			result: partitionRangeOR{{4, 5}, {6, 7}, {8, 10}}},
   249  	}
   250  	for i, ca := range testIntersection {
   251  		result := ca.input1.intersection(ca.input2)
   252  		c.Assert(equalPartitionRangeOR(ca.result, result), IsTrue, Commentf("failed %d", i))
   253  	}
   254  
   255  	testUnion := []struct {
   256  		input1 partitionRangeOR
   257  		input2 partitionRangeOR
   258  		result partitionRangeOR
   259  	}{
   260  		{input1: partitionRangeOR{{0, 1}, {2, 7}},
   261  			input2: partitionRangeOR{{3, 5}},
   262  			result: partitionRangeOR{{0, 1}, {2, 7}}},
   263  		{input1: partitionRangeOR{{2, 7}},
   264  			input2: partitionRangeOR{{0, 3}, {4, 12}},
   265  			result: partitionRangeOR{{0, 12}}},
   266  		{input1: partitionRangeOR{{4, 7}, {8, 10}},
   267  			input2: partitionRangeOR{{0, 5}},
   268  			result: partitionRangeOR{{0, 7}, {8, 10}}},
   269  	}
   270  	for i, ca := range testUnion {
   271  		result := ca.input1.union(ca.input2)
   272  		c.Assert(equalPartitionRangeOR(ca.result, result), IsTrue, Commentf("failed %d", i))
   273  	}
   274  }
   275  
   276  func (s *testPartitionPruningSuite) TestPartitionRangePrunner2VarChar(c *C) {
   277  	tc := prepareTestCtx(c,
   278  		"create causet t (a varchar(32))",
   279  		"a",
   280  	)
   281  	lessThanDataInt := []string{"'c'", "'f'", "'h'", "'l'", "'t'"}
   282  	lessThan := make([]memex.Expression, len(lessThanDataInt)+1) // +1 for maxvalue
   283  	for i, str := range lessThanDataInt {
   284  		tmp, err := memex.ParseSimpleExprsWithNames(tc.sctx, str, tc.schemaReplicant, tc.names)
   285  		c.Assert(err, IsNil)
   286  		lessThan[i] = tmp[0]
   287  	}
   288  
   289  	prunner := &rangeDeferredCausetsPruner{lessThan, tc.columns[0], true}
   290  	cases := []struct {
   291  		input  string
   292  		result partitionRangeOR
   293  	}{
   294  		{"a > 'g'", partitionRangeOR{{2, 6}}},
   295  		{"a < 'h'", partitionRangeOR{{0, 3}}},
   296  		{"a >= 'm'", partitionRangeOR{{4, 6}}},
   297  		{"a > 'm'", partitionRangeOR{{4, 6}}},
   298  		{"a < 'f'", partitionRangeOR{{0, 2}}},
   299  		{"a = 'c'", partitionRangeOR{{1, 2}}},
   300  		{"a > 't'", partitionRangeOR{{5, 6}}},
   301  		{"a > 'c' and a < 'q'", partitionRangeOR{{1, 5}}},
   302  		{"a < 'l' or a >= 'w'", partitionRangeOR{{0, 4}, {5, 6}}},
   303  		{"a is null", partitionRangeOR{{0, 1}}},
   304  		{"'mm' > a", partitionRangeOR{{0, 5}}},
   305  		{"'f' <= a", partitionRangeOR{{2, 6}}},
   306  		{"'f' >= a", partitionRangeOR{{0, 3}}},
   307  	}
   308  
   309  	for _, ca := range cases {
   310  		expr, err := memex.ParseSimpleExprsWithNames(tc.sctx, ca.input, tc.schemaReplicant, tc.names)
   311  		c.Assert(err, IsNil)
   312  		result := fullRange(len(lessThan))
   313  		result = partitionRangeForExpr(tc.sctx, expr[0], prunner, result)
   314  		c.Assert(equalPartitionRangeOR(ca.result, result), IsTrue, Commentf("unexpected:", ca.input))
   315  	}
   316  }
   317  
   318  func (s *testPartitionPruningSuite) TestPartitionRangePrunner2Date(c *C) {
   319  	tc := prepareTestCtx(c,
   320  		"create causet t (a date)",
   321  		"a",
   322  	)
   323  	lessThanDataInt := []string{
   324  		"'1999-06-01'",
   325  		"'2000-05-01'",
   326  		"'2008-04-01'",
   327  		"'2010-03-01'",
   328  		"'2020-02-01'",
   329  		"'2020-01-01'"}
   330  	lessThan := make([]memex.Expression, len(lessThanDataInt))
   331  	for i, str := range lessThanDataInt {
   332  		tmp, err := memex.ParseSimpleExprsWithNames(tc.sctx, str, tc.schemaReplicant, tc.names)
   333  		c.Assert(err, IsNil)
   334  		lessThan[i] = tmp[0]
   335  	}
   336  
   337  	prunner := &rangeDeferredCausetsPruner{lessThan, tc.columns[0], false}
   338  	cases := []struct {
   339  		input  string
   340  		result partitionRangeOR
   341  	}{
   342  		{"a < '1943-02-12'", partitionRangeOR{{0, 1}}},
   343  		{"a >= '1969-02-13'", partitionRangeOR{{0, 6}}},
   344  		{"a > '2003-03-13'", partitionRangeOR{{2, 6}}},
   345  		{"a < '2006-02-03'", partitionRangeOR{{0, 3}}},
   346  		{"a = '2007-07-07'", partitionRangeOR{{2, 3}}},
   347  		{"a > '1949-10-10'", partitionRangeOR{{0, 6}}},
   348  		{"a > '2020-02-01' and a < '2000-01-03'", partitionRangeOR{}},
   349  		{"a < '1969-11-12' or a >= '2020-09-18'", partitionRangeOR{{0, 1}, {5, 6}}},
   350  		{"a is null", partitionRangeOR{{0, 1}}},
   351  		{"'2003-02-27' >= a", partitionRangeOR{{0, 3}}},
   352  		{"'2020-10-24' < a", partitionRangeOR{{4, 6}}},
   353  		{"'2003-03-30' > a", partitionRangeOR{{0, 3}}},
   354  	}
   355  
   356  	for _, ca := range cases {
   357  		expr, err := memex.ParseSimpleExprsWithNames(tc.sctx, ca.input, tc.schemaReplicant, tc.names)
   358  		c.Assert(err, IsNil)
   359  		result := fullRange(len(lessThan))
   360  		result = partitionRangeForExpr(tc.sctx, expr[0], prunner, result)
   361  		c.Assert(equalPartitionRangeOR(ca.result, result), IsTrue, Commentf("unexpected:", ca.input))
   362  	}
   363  }