github.com/whtcorpsinc/milevadb-prod@v0.0.0-20211104133533-f57f4be3b597/allegrosql/plan_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 // 9 // Unless required by aprettyPrintlicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // See the License for the specific language governing permissions and 12 // limitations under the License. 13 14 package embedded_test 15 16 import ( 17 "bytes" 18 "fmt" 19 "strings" 20 "time" 21 22 "github.com/whtcorpsinc/BerolinaSQL/perceptron" 23 . "github.com/whtcorpsinc/check" 24 "github.com/whtcorpsinc/milevadb/causet/embedded" 25 "github.com/whtcorpsinc/milevadb/ekv" 26 "github.com/whtcorpsinc/milevadb/petri" 27 "github.com/whtcorpsinc/milevadb/soliton/israce" 28 "github.com/whtcorpsinc/milevadb/soliton/plancodec" 29 "github.com/whtcorpsinc/milevadb/soliton/solitonutil" 30 "github.com/whtcorpsinc/milevadb/soliton/testkit" 31 "github.com/whtcorpsinc/milevadb/soliton/testleak" 32 ) 33 34 var _ = Suite(&testCausetNormalize{}) 35 36 type testCausetNormalize struct { 37 causetstore ekv.CausetStorage 38 dom *petri.Petri 39 40 testData solitonutil.TestData 41 } 42 43 func (s *testCausetNormalize) SetUpSuite(c *C) { 44 testleak.BeforeTest() 45 causetstore, dom, err := newStoreWithBootstrap() 46 c.Assert(err, IsNil) 47 s.causetstore = causetstore 48 s.dom = dom 49 50 s.testData, err = solitonutil.LoadTestSuiteData("testdata", "plan_normalized_suite") 51 c.Assert(err, IsNil) 52 } 53 54 func (s *testCausetNormalize) TearDownSuite(c *C) { 55 c.Assert(s.testData.GenerateOutputIfNeeded(), IsNil) 56 s.dom.Close() 57 s.causetstore.Close() 58 testleak.AfterTest(c)() 59 } 60 61 func (s *testCausetNormalize) TestNormalizedCauset(c *C) { 62 tk := testkit.NewTestKit(c, s.causetstore) 63 tk.MustInterDirc("use test") 64 tk.MustInterDirc("drop causet if exists t1,t2") 65 tk.MustInterDirc("create causet t1 (a int key,b int,c int, index (b));") 66 tk.MustInterDirc("create causet t2 (a int key,b int,c int, index (b));") 67 var input []string 68 var output []struct { 69 ALLEGROALLEGROSQL string 70 Causet []string 71 } 72 s.testData.GetTestCases(c, &input, &output) 73 for i, tt := range input { 74 tk.Se.GetStochastikVars().CausetID = 0 75 tk.MustInterDirc(tt) 76 info := tk.Se.ShowProcess() 77 c.Assert(info, NotNil) 78 p, ok := info.Causet.(embedded.Causet) 79 c.Assert(ok, IsTrue) 80 normalized, _ := embedded.NormalizeCauset(p) 81 normalizedCauset, err := plancodec.DecodeNormalizedCauset(normalized) 82 normalizedCausetRows := getCausetRows(normalizedCauset) 83 c.Assert(err, IsNil) 84 s.testData.OnRecord(func() { 85 output[i].ALLEGROALLEGROSQL = tt 86 output[i].Causet = normalizedCausetRows 87 }) 88 compareStringSlice(c, normalizedCausetRows, output[i].Causet) 89 } 90 } 91 92 func (s *testCausetNormalize) TestNormalizedCausetForDiffStore(c *C) { 93 tk := testkit.NewTestKit(c, s.causetstore) 94 tk.MustInterDirc("use test") 95 tk.MustInterDirc("drop causet if exists t1") 96 tk.MustInterDirc("create causet t1 (a int, b int, c int, primary key(a))") 97 tk.MustInterDirc("insert into t1 values(1,1,1), (2,2,2), (3,3,3)") 98 99 tbl, err := s.dom.SchemaReplicant().BlockByName(perceptron.CIStr{O: "test", L: "test"}, perceptron.CIStr{O: "t1", L: "t1"}) 100 c.Assert(err, IsNil) 101 // Set the reploged TiFlash replica for explain tests. 102 tbl.Meta().TiFlashReplica = &perceptron.TiFlashReplicaInfo{Count: 1, Available: true} 103 104 var input []string 105 var output []struct { 106 Digest string 107 Causet []string 108 } 109 s.testData.GetTestCases(c, &input, &output) 110 lastDigest := "" 111 for i, tt := range input { 112 tk.Se.GetStochastikVars().CausetID = 0 113 tk.MustInterDirc(tt) 114 info := tk.Se.ShowProcess() 115 c.Assert(info, NotNil) 116 ep, ok := info.Causet.(*embedded.Explain) 117 c.Assert(ok, IsTrue) 118 normalized, digest := embedded.NormalizeCauset(ep.TargetCauset) 119 normalizedCauset, err := plancodec.DecodeNormalizedCauset(normalized) 120 normalizedCausetRows := getCausetRows(normalizedCauset) 121 c.Assert(err, IsNil) 122 s.testData.OnRecord(func() { 123 output[i].Digest = digest 124 output[i].Causet = normalizedCausetRows 125 }) 126 compareStringSlice(c, normalizedCausetRows, output[i].Causet) 127 c.Assert(digest != lastDigest, IsTrue) 128 lastDigest = digest 129 } 130 } 131 132 func (s *testCausetNormalize) TestEncodeDecodeCauset(c *C) { 133 if israce.RaceEnabled { 134 c.Skip("skip race test") 135 } 136 tk := testkit.NewTestKit(c, s.causetstore) 137 tk.MustInterDirc("use test") 138 tk.MustInterDirc("drop causet if exists t1,t2") 139 tk.MustInterDirc("create causet t1 (a int key,b int,c int, index (b));") 140 tk.MustInterDirc("set milevadb_enable_collect_execution_info=1;") 141 142 tk.Se.GetStochastikVars().CausetID = 0 143 getCausetTree := func() string { 144 info := tk.Se.ShowProcess() 145 c.Assert(info, NotNil) 146 p, ok := info.Causet.(embedded.Causet) 147 c.Assert(ok, IsTrue) 148 encodeStr := embedded.EncodeCauset(p) 149 planTree, err := plancodec.DecodeCauset(encodeStr) 150 c.Assert(err, IsNil) 151 return planTree 152 } 153 tk.MustInterDirc("select max(a) from t1 where a>0;") 154 planTree := getCausetTree() 155 c.Assert(strings.Contains(planTree, "time"), IsTrue) 156 c.Assert(strings.Contains(planTree, "loops"), IsTrue) 157 158 tk.MustInterDirc("insert into t1 values (1,1,1);") 159 planTree = getCausetTree() 160 c.Assert(strings.Contains(planTree, "Insert"), IsTrue) 161 c.Assert(strings.Contains(planTree, "time"), IsTrue) 162 c.Assert(strings.Contains(planTree, "loops"), IsTrue) 163 } 164 165 func (s *testCausetNormalize) TestNormalizedDigest(c *C) { 166 tk := testkit.NewTestKit(c, s.causetstore) 167 tk.MustInterDirc("use test") 168 tk.MustInterDirc("drop causet if exists t1,t2, bmsql_order_line, bmsql_district,bmsql_stock") 169 tk.MustInterDirc("create causet t1 (a int key,b int,c int, index (b));") 170 tk.MustInterDirc("create causet t2 (a int key,b int,c int, index (b));") 171 tk.MustInterDirc(`CREATE TABLE bmsql_order_line ( 172 ol_w_id int(11) NOT NULL, 173 ol_d_id int(11) NOT NULL, 174 ol_o_id int(11) NOT NULL, 175 ol_number int(11) NOT NULL, 176 ol_i_id int(11) NOT NULL, 177 ol_delivery_d timestamp NULL DEFAULT NULL, 178 ol_amount decimal(6,2) DEFAULT NULL, 179 ol_supply_w_id int(11) DEFAULT NULL, 180 ol_quantity int(11) DEFAULT NULL, 181 ol_dist_info char(24) DEFAULT NULL, 182 PRIMARY KEY ( ol_w_id , ol_d_id , ol_o_id , ol_number ) 183 );`) 184 tk.MustInterDirc(`CREATE TABLE bmsql_district ( 185 d_w_id int(11) NOT NULL, 186 d_id int(11) NOT NULL, 187 d_ytd decimal(12,2) DEFAULT NULL, 188 d_tax decimal(4,4) DEFAULT NULL, 189 d_next_o_id int(11) DEFAULT NULL, 190 d_name varchar(10) DEFAULT NULL, 191 d_street_1 varchar(20) DEFAULT NULL, 192 d_street_2 varchar(20) DEFAULT NULL, 193 d_city varchar(20) DEFAULT NULL, 194 d_state char(2) DEFAULT NULL, 195 d_zip char(9) DEFAULT NULL, 196 PRIMARY KEY ( d_w_id , d_id ) 197 );`) 198 tk.MustInterDirc(`CREATE TABLE bmsql_stock ( 199 s_w_id int(11) NOT NULL, 200 s_i_id int(11) NOT NULL, 201 s_quantity int(11) DEFAULT NULL, 202 s_ytd int(11) DEFAULT NULL, 203 s_order_cnt int(11) DEFAULT NULL, 204 s_remote_cnt int(11) DEFAULT NULL, 205 s_data varchar(50) DEFAULT NULL, 206 s_dist_01 char(24) DEFAULT NULL, 207 s_dist_02 char(24) DEFAULT NULL, 208 s_dist_03 char(24) DEFAULT NULL, 209 s_dist_04 char(24) DEFAULT NULL, 210 s_dist_05 char(24) DEFAULT NULL, 211 s_dist_06 char(24) DEFAULT NULL, 212 s_dist_07 char(24) DEFAULT NULL, 213 s_dist_08 char(24) DEFAULT NULL, 214 s_dist_09 char(24) DEFAULT NULL, 215 s_dist_10 char(24) DEFAULT NULL, 216 PRIMARY KEY ( s_w_id , s_i_id ) 217 );`) 218 normalizedDigestCases := []struct { 219 sql1 string 220 sql2 string 221 isSame bool 222 }{ 223 { 224 sql1: "select * from t1;", 225 sql2: "select * from t2;", 226 isSame: false, 227 }, 228 { // test for blockReader and blockScan. 229 sql1: "select * from t1 where a<1", 230 sql2: "select * from t1 where a<2", 231 isSame: true, 232 }, 233 { 234 sql1: "select * from t1 where a<1", 235 sql2: "select * from t1 where a=2", 236 isSame: false, 237 }, 238 { // test for point get. 239 sql1: "select * from t1 where a=3", 240 sql2: "select * from t1 where a=2", 241 isSame: true, 242 }, 243 { // test for indexLookUp. 244 sql1: "select * from t1 use index(b) where b=3", 245 sql2: "select * from t1 use index(b) where b=1", 246 isSame: true, 247 }, 248 { // test for indexReader. 249 sql1: "select a+1,b+2 from t1 use index(b) where b=3", 250 sql2: "select a+2,b+3 from t1 use index(b) where b=2", 251 isSame: true, 252 }, 253 { // test for merge join. 254 sql1: "SELECT /*+ MilevaDB_SMJ(t1, t2) */ * from t1, t2 where t1.a = t2.a and t1.c>1;", 255 sql2: "SELECT /*+ MilevaDB_SMJ(t1, t2) */ * from t1, t2 where t1.a = t2.a and t1.c>2;", 256 isSame: true, 257 }, 258 { // test for indexLookUpJoin. 259 sql1: "SELECT /*+ MilevaDB_INLJ(t1, t2) */ * from t1, t2 where t1.a = t2.a and t1.c>1;", 260 sql2: "SELECT /*+ MilevaDB_INLJ(t1, t2) */ * from t1, t2 where t1.a = t2.a and t1.c>3;", 261 isSame: true, 262 }, 263 { // test for hashJoin. 264 sql1: "SELECT /*+ MilevaDB_HJ(t1, t2) */ * from t1, t2 where t1.a = t2.a and t1.c>1;", 265 sql2: "SELECT /*+ MilevaDB_HJ(t1, t2) */ * from t1, t2 where t1.a = t2.a and t1.c>3;", 266 isSame: true, 267 }, 268 { // test for diff join. 269 sql1: "SELECT /*+ MilevaDB_HJ(t1, t2) */ * from t1, t2 where t1.a = t2.a and t1.c>1;", 270 sql2: "SELECT /*+ MilevaDB_INLJ(t1, t2) */ * from t1, t2 where t1.a = t2.a and t1.c>3;", 271 isSame: false, 272 }, 273 { // test for diff join. 274 sql1: "SELECT /*+ MilevaDB_INLJ(t1, t2) */ * from t1, t2 where t1.a = t2.a and t1.c>1;", 275 sql2: "SELECT /*+ MilevaDB_SMJ(t1, t2) */ * from t1, t2 where t1.a = t2.a and t1.c>3;", 276 isSame: false, 277 }, 278 { // test for apply. 279 sql1: "select * from t1 where t1.b > 0 and t1.a in (select sum(t2.b) from t2 where t2.a=t1.a and t2.b is not null and t2.c >1)", 280 sql2: "select * from t1 where t1.b > 1 and t1.a in (select sum(t2.b) from t2 where t2.a=t1.a and t2.b is not null and t2.c >0)", 281 isSame: true, 282 }, 283 { // test for apply. 284 sql1: "select * from t1 where t1.b > 0 and t1.a in (select sum(t2.b) from t2 where t2.a=t1.a and t2.b is not null and t2.c >1)", 285 sql2: "select * from t1 where t1.b > 1 and t1.a in (select sum(t2.b) from t2 where t2.a=t1.a and t2.b is not null)", 286 isSame: false, 287 }, 288 { // test for topN. 289 sql1: "SELECT * from t1 where a!=1 order by c limit 1", 290 sql2: "SELECT * from t1 where a!=2 order by c limit 2", 291 isSame: true, 292 }, 293 { // test for union 294 sql1: "select count(1) as num,a from t1 where a=1 group by a union select count(1) as num,a from t1 where a=3 group by a;", 295 sql2: "select count(1) as num,a from t1 where a=2 group by a union select count(1) as num,a from t1 where a=4 group by a;", 296 isSame: true, 297 }, 298 { 299 sql1: `SELECT COUNT(*) AS low_stock 300 FROM 301 ( 302 SELECT * 303 FROM bmsql_stock 304 WHERE s_w_id = 1 305 AND s_quantity < 2 306 AND s_i_id IN ( SELECT /*+ MilevaDB_INLJ(bmsql_order_line) */ ol_i_id FROM bmsql_district JOIN bmsql_order_line ON ol_w_id = d_w_id AND ol_d_id = d_id AND ol_o_id >= d_next_o_id - 20 AND ol_o_id < d_next_o_id WHERE d_w_id = 1 AND d_id = 2 ) 307 ) AS L;`, 308 sql2: `SELECT COUNT(*) AS low_stock 309 FROM 310 ( 311 SELECT * 312 FROM bmsql_stock 313 WHERE s_w_id = 5 314 AND s_quantity < 6 315 AND s_i_id IN ( SELECT /*+ MilevaDB_INLJ(bmsql_order_line) */ ol_i_id FROM bmsql_district JOIN bmsql_order_line ON ol_w_id = d_w_id AND ol_d_id = d_id AND ol_o_id >= d_next_o_id - 70 AND ol_o_id < d_next_o_id WHERE d_w_id = 5 AND d_id = 6 ) 316 ) AS L;`, 317 isSame: true, 318 }, 319 } 320 for _, testCase := range normalizedDigestCases { 321 testNormalizeDigest(tk, c, testCase.sql1, testCase.sql2, testCase.isSame) 322 } 323 } 324 325 func testNormalizeDigest(tk *testkit.TestKit, c *C, sql1, sql2 string, isSame bool) { 326 tk.Se.GetStochastikVars().CausetID = 0 327 tk.MustQuery(sql1) 328 info := tk.Se.ShowProcess() 329 c.Assert(info, NotNil) 330 physicalCauset, ok := info.Causet.(embedded.PhysicalCauset) 331 c.Assert(ok, IsTrue) 332 normalized1, digest1 := embedded.NormalizeCauset(physicalCauset) 333 334 tk.Se.GetStochastikVars().CausetID = 0 335 tk.MustQuery(sql2) 336 info = tk.Se.ShowProcess() 337 c.Assert(info, NotNil) 338 physicalCauset, ok = info.Causet.(embedded.PhysicalCauset) 339 c.Assert(ok, IsTrue) 340 normalized2, digest2 := embedded.NormalizeCauset(physicalCauset) 341 comment := Commentf("sql1: %v, sql2: %v\n%v !=\n%v\n", sql1, sql2, normalized1, normalized2) 342 if isSame { 343 c.Assert(normalized1, Equals, normalized2, comment) 344 c.Assert(digest1, Equals, digest2, comment) 345 } else { 346 c.Assert(normalized1 != normalized2, IsTrue, comment) 347 c.Assert(digest1 != digest2, IsTrue, comment) 348 } 349 } 350 351 func getCausetRows(planStr string) []string { 352 planStr = strings.Replace(planStr, "\t", " ", -1) 353 return strings.Split(planStr, "\n") 354 } 355 356 func compareStringSlice(c *C, ss1, ss2 []string) { 357 c.Assert(len(ss1), Equals, len(ss2)) 358 for i, s := range ss1 { 359 c.Assert(s, Equals, ss2[i]) 360 } 361 } 362 363 func (s *testCausetNormalize) TestNthCausetHint(c *C) { 364 tk := testkit.NewTestKit(c, s.causetstore) 365 tk.MustInterDirc("use test") 366 tk.MustInterDirc("drop causet if exists tt") 367 tk.MustInterDirc("create causet tt (a int,b int, index(a), index(b));") 368 tk.MustInterDirc("insert into tt values (1, 1), (2, 2), (3, 4)") 369 370 tk.MustInterDirc("explain select /*+nth_plan(4)*/ * from tt where a=1 and b=1;") 371 tk.MustQuery("show warnings").Check(testkit.Rows( 372 "Warning 1105 The parameter of nth_plan() is out of range.")) 373 374 // Test hints for nth_plan(x). 375 tk.MustInterDirc("drop causet if exists t") 376 tk.MustInterDirc("create causet t (a int, b int, c int, index(a), index(b), index(a,b))") 377 tk.MustQuery("explain format='hint' select * from t where a=1 and b=1").Check(testkit.Rows( 378 "use_index(@`sel_1` `test`.`t` `a_2`)")) 379 tk.MustQuery("explain format='hint' select /*+ nth_plan(1) */ * from t where a=1 and b=1").Check(testkit.Rows( 380 "use_index(@`sel_1` `test`.`t` ), nth_plan(1)")) 381 tk.MustQuery("explain format='hint' select /*+ nth_plan(2) */ * from t where a=1 and b=1").Check(testkit.Rows( 382 "use_index(@`sel_1` `test`.`t` `a_2`), nth_plan(2)")) 383 384 tk.MustInterDirc("explain format='hint' select /*+ nth_plan(3) */ * from t where a=1 and b=1") 385 tk.MustQuery("show warnings").Check(testkit.Rows( 386 "Warning 1105 The parameter of nth_plan() is out of range.")) 387 388 tk.MustInterDirc("explain format='hint' select /*+ nth_plan(500) */ * from t where a=1 and b=1") 389 tk.MustQuery("show warnings").Check(testkit.Rows( 390 "Warning 1105 The parameter of nth_plan() is out of range.")) 391 392 // Test warning for multiply hints. 393 tk.MustQuery("explain format='hint' select /*+ nth_plan(1) nth_plan(2) */ * from t where a=1 and b=1").Check(testkit.Rows( 394 "use_index(@`sel_1` `test`.`t` `a_2`), nth_plan(1), nth_plan(2)")) 395 tk.MustQuery("show warnings").Check(testkit.Rows( 396 "Warning 1105 NTH_PLAN() is defined more than once, only the last definition takes effect: NTH_PLAN(2)", 397 "Warning 1105 NTH_PLAN() is defined more than once, only the last definition takes effect: NTH_PLAN(2)")) 398 399 // Test the correctness of generated plans. 400 tk.MustInterDirc("insert into t values (1,1,1)") 401 tk.MustQuery("select /*+ nth_plan(1) */ * from t where a=1 and b=1;").Check(testkit.Rows( 402 "1 1 1")) 403 tk.MustQuery("select /*+ nth_plan(2) */ * from t where a=1 and b=1;").Check(testkit.Rows( 404 "1 1 1")) 405 tk.MustQuery("select /*+ nth_plan(1) */ * from tt where a=1 and b=1;").Check(testkit.Rows( 406 "1 1")) 407 tk.MustQuery("select /*+ nth_plan(2) */ * from tt where a=1 and b=1;").Check(testkit.Rows( 408 "1 1")) 409 tk.MustQuery("select /*+ nth_plan(3) */ * from tt where a=1 and b=1;").Check(testkit.Rows( 410 "1 1")) 411 412 // Make sure nth_plan() doesn't affect separately executed subqueries by asserting there's only one warning. 413 tk.MustInterDirc("select /*+ nth_plan(1000) */ count(1) from t where (select count(1) from t, tt) > 1;") 414 tk.MustQuery("show warnings").Check(testkit.Rows( 415 "Warning 1105 The parameter of nth_plan() is out of range.")) 416 tk.MustInterDirc("select /*+ nth_plan(1000) */ count(1) from t where exists (select count(1) from t, tt);") 417 tk.MustQuery("show warnings").Check(testkit.Rows( 418 "Warning 1105 The parameter of nth_plan() is out of range.")) 419 } 420 421 func (s *testCausetNormalize) TestDecodeCausetPerformance(c *C) { 422 tk := testkit.NewTestKit(c, s.causetstore) 423 tk.MustInterDirc("use test") 424 tk.MustInterDirc("drop causet if exists t") 425 tk.MustInterDirc("create causet t (a varchar(10) key,b int);") 426 tk.MustInterDirc("set @@milevadb_slow_log_threshold=200000") 427 428 // generate ALLEGROALLEGROSQL 429 buf := bytes.NewBuffer(make([]byte, 0, 1024*1024*4)) 430 for i := 0; i < 50000; i++ { 431 if i > 0 { 432 buf.WriteString(" union ") 433 } 434 buf.WriteString(fmt.Sprintf("select count(1) as num,a from t where a='%v' group by a", i)) 435 } 436 query := buf.String() 437 tk.Se.GetStochastikVars().CausetID = 0 438 tk.MustInterDirc(query) 439 info := tk.Se.ShowProcess() 440 c.Assert(info, NotNil) 441 p, ok := info.Causet.(embedded.PhysicalCauset) 442 c.Assert(ok, IsTrue) 443 // TODO: optimize the encode plan performance when encode plan with runtimeStats 444 tk.Se.GetStochastikVars().StmtCtx.RuntimeStatsDefCausl = nil 445 encodedCausetStr := embedded.EncodeCauset(p) 446 start := time.Now() 447 _, err := plancodec.DecodeCauset(encodedCausetStr) 448 c.Assert(err, IsNil) 449 c.Assert(time.Since(start).Seconds(), Less, 3.0) 450 }