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 }