github.com/matrixorigin/matrixone@v1.2.0/pkg/sql/plan/build_test.go (about) 1 // Copyright 2021 - 2022 Matrix Origin 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 applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package plan 16 17 import ( 18 "bytes" 19 "context" 20 "encoding/json" 21 "go/constant" 22 "os" 23 "strings" 24 "testing" 25 26 "github.com/matrixorigin/matrixone/pkg/container/types" 27 "github.com/matrixorigin/matrixone/pkg/pb/plan" 28 "github.com/matrixorigin/matrixone/pkg/sql/parsers/dialect/mysql" 29 "github.com/matrixorigin/matrixone/pkg/sql/parsers/tree" 30 "github.com/matrixorigin/matrixone/pkg/testutil" 31 "github.com/stretchr/testify/assert" 32 ) 33 34 func BenchmarkInsert(b *testing.B) { 35 typ := types.T_varchar.ToType() 36 typ.Width = 1024 37 targetType := makePlan2Type(&typ) 38 targetType.Width = 1024 39 40 originStr := "0123456789" 41 testExpr := tree.NewNumValWithType(constant.MakeString(originStr), originStr, false, tree.P_char) 42 targetT := &plan.Expr{ 43 Typ: targetType, 44 Expr: &plan.Expr_T{ 45 T: &plan.TargetType{}, 46 }, 47 } 48 ctx := context.TODO() 49 for i := 0; i < b.N; i++ { 50 binder := NewDefaultBinder(ctx, nil, nil, targetType, nil) 51 expr, err := binder.BindExpr(testExpr, 0, true) 52 if err != nil { 53 break 54 } 55 _, err = forceCastExpr2(ctx, expr, typ, targetT) 56 if err != nil { 57 break 58 } 59 } 60 } 61 62 // only use in developing 63 func TestSingleSQL(t *testing.T) { 64 // sql := "INSERT INTO NATION VALUES (1, 'NAME1',21, 'COMMENT1'), (2, 'NAME2', 22, 'COMMENT2')" 65 // sql := "insert into dept values (11, 'aa', 'bb')" 66 // sql := "delete from dept where deptno > 10" 67 // sql := "delete from nation where n_nationkey > 10" 68 // sql := "delete nation, nation2 from nation join nation2 on nation.n_name = nation2.n_name" 69 // sql := "update nation set n_name ='a' where n_nationkey > 10" 70 // sql := "update dept set deptno = 11 where deptno = 10" 71 sqls := []string{"prepare stmt1 from update nation set n_name = ? where n_nationkey = ?", 72 "prepare stmt1 from insert into nation values (?, ?, ?, ?) ON DUPLICATE KEY UPDATE n_name=?"} 73 mock := NewMockOptimizer(true) 74 75 for _, sql := range sqls { 76 logicPlan, err := runOneStmt(mock, t, sql) 77 if err != nil { 78 t.Fatalf("%+v", err) 79 } 80 outPutPlan(logicPlan, true, t) 81 } 82 } 83 84 //Test Query Node Tree 85 // func TestNodeTree(t *testing.T) { 86 // type queryCheck struct { 87 // steps []int32 //steps 88 // nodeType map[int]plan.Node_NodeType //node_type in each node 89 // children map[int][]int32 //children in each node 90 // } 91 92 // // map[sql string]checkData 93 // nodeTreeCheckList := map[string]queryCheck{ 94 // "SELECT -1": { 95 // steps: []int32{0}, 96 // nodeType: map[int]plan.Node_NodeType{ 97 // 0: plan.Node_VALUE_SCAN, 98 // }, 99 // children: nil, 100 // }, 101 // "SELECT -1 from dual": { 102 // steps: []int32{0}, 103 // nodeType: map[int]plan.Node_NodeType{ 104 // 0: plan.Node_VALUE_SCAN, 105 // }, 106 // children: nil, 107 // }, 108 // // one node 109 // "SELECT N_NAME FROM NATION WHERE N_REGIONKEY = 3": { 110 // steps: []int32{0}, 111 // nodeType: map[int]plan.Node_NodeType{ 112 // 0: plan.Node_TABLE_SCAN, 113 // }, 114 // children: nil, 115 // }, 116 // // two nodes- SCAN + SORT 117 // "SELECT N_NAME FROM NATION WHERE N_REGIONKEY = 3 Order By N_REGIONKEY": { 118 // steps: []int32{1}, 119 // nodeType: map[int]plan.Node_NodeType{ 120 // 0: plan.Node_TABLE_SCAN, 121 // 1: plan.Node_SORT, 122 // }, 123 // children: map[int][]int32{ 124 // 1: {0}, 125 // }, 126 // }, 127 // // two nodes- SCAN + AGG(group by) 128 // "SELECT N_NAME FROM NATION WHERE N_REGIONKEY = 3 Group By N_NAME": { 129 // steps: []int32{1}, 130 // nodeType: map[int]plan.Node_NodeType{ 131 // 0: plan.Node_TABLE_SCAN, 132 // 1: plan.Node_AGG, 133 // }, 134 // children: map[int][]int32{ 135 // 1: {0}, 136 // }, 137 // }, 138 // "select sum(n_nationkey) from nation": { 139 // steps: []int32{1}, 140 // nodeType: map[int]plan.Node_NodeType{ 141 // 0: plan.Node_TABLE_SCAN, 142 // 1: plan.Node_AGG, 143 // }, 144 // children: map[int][]int32{ 145 // 1: {0}, 146 // }, 147 // }, 148 // "select sum(n_nationkey) from nation order by sum(n_nationkey)": { 149 // steps: []int32{2}, 150 // nodeType: map[int]plan.Node_NodeType{ 151 // 0: plan.Node_TABLE_SCAN, 152 // 1: plan.Node_AGG, 153 // 2: plan.Node_SORT, 154 // }, 155 // children: map[int][]int32{ 156 // 1: {0}, 157 // 2: {1}, 158 // }, 159 // }, 160 // // two nodes- SCAN + AGG(distinct) 161 // "SELECT distinct N_NAME FROM NATION": { 162 // steps: []int32{1}, 163 // nodeType: map[int]plan.Node_NodeType{ 164 // 0: plan.Node_TABLE_SCAN, 165 // 1: plan.Node_AGG, 166 // }, 167 // children: map[int][]int32{ 168 // 1: {0}, 169 // }, 170 // }, 171 // // three nodes- SCAN + AGG(group by) + SORT 172 // "SELECT N_NAME, count(*) as ttl FROM NATION Group By N_NAME Order By ttl": { 173 // steps: []int32{2}, 174 // nodeType: map[int]plan.Node_NodeType{ 175 // 0: plan.Node_TABLE_SCAN, 176 // 1: plan.Node_AGG, 177 // 2: plan.Node_SORT, 178 // }, 179 // children: map[int][]int32{ 180 // 1: {0}, 181 // 2: {1}, 182 // }, 183 // }, 184 // // three nodes - SCAN, SCAN, JOIN 185 // "SELECT N_NAME, N_REGIONKEY FROM NATION join REGION on NATION.N_REGIONKEY = REGION.R_REGIONKEY": { 186 // steps: []int32{3}, 187 // nodeType: map[int]plan.Node_NodeType{ 188 // 0: plan.Node_TABLE_SCAN, 189 // 1: plan.Node_TABLE_SCAN, 190 // 2: plan.Node_JOIN, 191 // 3: plan.Node_PROJECT, 192 // }, 193 // children: map[int][]int32{ 194 // 2: {0, 1}, 195 // }, 196 // }, 197 // // three nodes - SCAN, SCAN, JOIN //use where for join condition 198 // "SELECT N_NAME, N_REGIONKEY FROM NATION, REGION WHERE NATION.N_REGIONKEY = REGION.R_REGIONKEY": { 199 // steps: []int32{3}, 200 // nodeType: map[int]plan.Node_NodeType{ 201 // 0: plan.Node_TABLE_SCAN, 202 // 1: plan.Node_TABLE_SCAN, 203 // 2: plan.Node_JOIN, 204 // 3: plan.Node_PROJECT, 205 // }, 206 // children: map[int][]int32{ 207 // 2: {0, 1}, 208 // 3: {2}, 209 // }, 210 // }, 211 // // 5 nodes - SCAN, SCAN, JOIN, SCAN, JOIN //join three table 212 // "SELECT l.L_ORDERKEY FROM CUSTOMER c, ORDERS o, LINEITEM l WHERE c.C_CUSTKEY = o.O_CUSTKEY and l.L_ORDERKEY = o.O_ORDERKEY and o.O_ORDERKEY < 10": { 213 // steps: []int32{6}, 214 // nodeType: map[int]plan.Node_NodeType{ 215 // 0: plan.Node_TABLE_SCAN, 216 // 1: plan.Node_TABLE_SCAN, 217 // 2: plan.Node_JOIN, 218 // 3: plan.Node_PROJECT, 219 // 4: plan.Node_TABLE_SCAN, 220 // 5: plan.Node_JOIN, 221 // 6: plan.Node_PROJECT, 222 // }, 223 // children: map[int][]int32{ 224 // 2: {0, 1}, 225 // 3: {2}, 226 // 5: {3, 4}, 227 // 6: {5}, 228 // }, 229 // }, 230 // // 6 nodes - SCAN, SCAN, JOIN, SCAN, JOIN, SORT //join three table 231 // "SELECT l.L_ORDERKEY FROM CUSTOMER c, ORDERS o, LINEITEM l WHERE c.C_CUSTKEY = o.O_CUSTKEY and l.L_ORDERKEY = o.O_ORDERKEY and o.O_ORDERKEY < 10 order by c.C_CUSTKEY": { 232 // steps: []int32{7}, 233 // nodeType: map[int]plan.Node_NodeType{ 234 // 0: plan.Node_TABLE_SCAN, 235 // 1: plan.Node_TABLE_SCAN, 236 // 2: plan.Node_JOIN, 237 // 3: plan.Node_PROJECT, 238 // 4: plan.Node_TABLE_SCAN, 239 // 5: plan.Node_JOIN, 240 // 6: plan.Node_PROJECT, 241 // 7: plan.Node_SORT, 242 // }, 243 // children: map[int][]int32{ 244 // 2: {0, 1}, 245 // 3: {2}, 246 // 5: {3, 4}, 247 // 6: {5}, 248 // 7: {6}, 249 // }, 250 // }, 251 // // 3 nodes //Derived table 252 // "select c_custkey from (select c_custkey, count(C_NATIONKEY) ff from CUSTOMER group by c_custkey) a where ff > 0": { 253 // steps: []int32{2}, 254 // nodeType: map[int]plan.Node_NodeType{ 255 // 0: plan.Node_TABLE_SCAN, 256 // 1: plan.Node_AGG, 257 // 2: plan.Node_PROJECT, 258 // }, 259 // children: map[int][]int32{ 260 // 1: {0}, 261 // 2: {1}, 262 // }, 263 // }, 264 // // 4 nodes //Derived table 265 // "select c_custkey from (select c_custkey, count(C_NATIONKEY) ff from CUSTOMER group by c_custkey ) a where ff > 0 order by c_custkey": { 266 // steps: []int32{3}, 267 // nodeType: map[int]plan.Node_NodeType{ 268 // 0: plan.Node_TABLE_SCAN, 269 // 1: plan.Node_AGG, 270 // 2: plan.Node_PROJECT, 271 // 3: plan.Node_SORT, 272 // }, 273 // children: map[int][]int32{ 274 // 1: {0}, 275 // 2: {1}, 276 // 3: {2}, 277 // }, 278 // }, 279 // // Derived table join normal table 280 // "select c_custkey from (select c_custkey, count(C_NATIONKEY) ff from CUSTOMER group by c_custkey ) a join NATION b on a.c_custkey = b.N_REGIONKEY where b.N_NATIONKEY > 10 order By b.N_REGIONKEY": { 281 // steps: []int32{6}, 282 // nodeType: map[int]plan.Node_NodeType{ 283 // 0: plan.Node_TABLE_SCAN, 284 // 1: plan.Node_AGG, 285 // 2: plan.Node_PROJECT, 286 // 3: plan.Node_TABLE_SCAN, 287 // 4: plan.Node_JOIN, 288 // 5: plan.Node_PROJECT, 289 // 6: plan.Node_SORT, 290 // }, 291 // children: map[int][]int32{ 292 // 1: {0}, 293 // 2: {1}, 294 // 4: {2, 3}, 295 // 5: {4}, 296 // 6: {5}, 297 // }, 298 // }, 299 // // insert from values 300 // "INSERT NATION (N_NATIONKEY, N_REGIONKEY, N_NAME) VALUES (1, 21, 'NAME1'), (2, 22, 'NAME2')": { 301 // steps: []int32{1}, 302 // nodeType: map[int]plan.Node_NodeType{ 303 // 0: plan.Node_VALUE_SCAN, 304 // 1: plan.Node_INSERT, 305 // }, 306 // children: map[int][]int32{ 307 // 1: {0}, 308 // }, 309 // }, 310 // // insert from select 311 // "INSERT NATION SELECT * FROM NATION2": { 312 // steps: []int32{1}, 313 // nodeType: map[int]plan.Node_NodeType{ 314 // 0: plan.Node_TABLE_SCAN, 315 // 1: plan.Node_INSERT, 316 // }, 317 // children: map[int][]int32{ 318 // 1: {0}, 319 // }, 320 // }, 321 // // update 322 // "UPDATE NATION SET N_NAME ='U1', N_REGIONKEY=N_REGIONKEY+2 WHERE N_NATIONKEY > 10 LIMIT 20": { 323 // steps: []int32{1}, 324 // nodeType: map[int]plan.Node_NodeType{ 325 // 0: plan.Node_TABLE_SCAN, 326 // 1: plan.Node_UPDATE, 327 // }, 328 // children: map[int][]int32{ 329 // 1: {0}, 330 // }, 331 // }, 332 // // delete 333 // "DELETE FROM NATION WHERE N_NATIONKEY > 10 LIMIT 20": { 334 // steps: []int32{1}, 335 // nodeType: map[int]plan.Node_NodeType{ 336 // 0: plan.Node_TABLE_SCAN, 337 // 1: plan.Node_DELETE, 338 // }, 339 // }, 340 // // uncorrelated subquery 341 // "SELECT * FROM NATION where N_REGIONKEY > (select max(R_REGIONKEY) from REGION)": { 342 // steps: []int32{0}, 343 // nodeType: map[int]plan.Node_NodeType{ 344 // 0: plan.Node_TABLE_SCAN, //nodeid = 1 here is the subquery 345 // 1: plan.Node_TABLE_SCAN, //nodeid = 0, here is SELECT * FROM NATION where N_REGIONKEY > [subquery] 346 // }, 347 // children: map[int][]int32{}, 348 // }, 349 // // correlated subquery 350 // `SELECT * FROM NATION where N_REGIONKEY > 351 // (select avg(R_REGIONKEY) from REGION where R_REGIONKEY < N_REGIONKEY group by R_NAME) 352 // order by N_NATIONKEY`: { 353 // steps: []int32{3}, 354 // nodeType: map[int]plan.Node_NodeType{ 355 // 0: plan.Node_TABLE_SCAN, //nodeid = 1 subquery node,so,wo pop it to top 356 // 1: plan.Node_TABLE_SCAN, //nodeid = 0 357 // 2: plan.Node_AGG, //nodeid = 2 subquery node,so,wo pop it to top 358 // 3: plan.Node_SORT, //nodeid = 3 359 // }, 360 // children: map[int][]int32{ 361 // 2: {1}, //nodeid = 2, have children(NodeId=1, position=0) 362 // 3: {0}, //nodeid = 3, have children(NodeId=0, position=2) 363 // }, 364 // }, 365 // // cte 366 // `with tbl(col1, col2) as (select n_nationkey, n_name from nation) select * from tbl order by col2`: { 367 // steps: []int32{1, 3}, 368 // nodeType: map[int]plan.Node_NodeType{ 369 // 0: plan.Node_TABLE_SCAN, 370 // 1: plan.Node_MATERIAL, 371 // 2: plan.Node_MATERIAL_SCAN, 372 // 3: plan.Node_SORT, 373 // }, 374 // children: map[int][]int32{ 375 // 1: {0}, 376 // 3: {2}, 377 // }, 378 // }, 379 // } 380 381 // // run test and check node tree 382 // for sql, check := range nodeTreeCheckList { 383 // mock := NewMockOptimizer(false) 384 // logicPlan, err := runOneStmt(mock, t, sql) 385 // query := logicPlan.GetQuery() 386 // if err != nil { 387 // t.Fatalf("%+v, sql=%v", err, sql) 388 // } 389 // if len(query.Steps) != len(check.steps) { 390 // t.Fatalf("run sql[%+v] error, root should be [%+v] but now is [%+v]", sql, check.steps, query.Steps) 391 // } 392 // for idx, step := range query.Steps { 393 // if step != check.steps[idx] { 394 // t.Fatalf("run sql[%+v] error, root should be [%+v] but now is [%+v]", sql, check.steps, query.Steps) 395 // } 396 // } 397 // for idx, typ := range check.nodeType { 398 // if idx >= len(query.Nodes) { 399 // t.Fatalf("run sql[%+v] error, query.Nodes[%+v].NodeType not exist", sql, idx) 400 // } 401 // if query.Nodes[idx].NodeType != typ { 402 // t.Fatalf("run sql[%+v] error, query.Nodes[%+v].NodeType should be [%+v] but now is [%+v]", sql, idx, typ, query.Nodes[idx].NodeType) 403 // } 404 // } 405 // for idx, children := range check.children { 406 // if idx >= len(query.Nodes) { 407 // t.Fatalf("run sql[%+v] error, query.Nodes[%+v].NodeType not exist", sql, idx) 408 // } 409 // if !reflect.DeepEqual(query.Nodes[idx].Children, children) { 410 // t.Fatalf("run sql[%+v] error, query.Nodes[%+v].Children should be [%+v] but now is [%+v]", sql, idx, children, query.Nodes[idx].Children) 411 // } 412 // } 413 // } 414 // } 415 416 // test single table plan building 417 func TestSingleTableSQLBuilder(t *testing.T) { 418 mock := NewMockOptimizer(false) 419 // should pass 420 sqls := []string{ 421 "SELECT '1900-01-01 00:00:00' + INTERVAL 2147483648 SECOND", 422 "SELECT N_NAME, N_REGIONKEY FROM NATION WHERE N_REGIONKEY > 0 AND N_NAME LIKE '%AA' ORDER BY N_NAME DESC, N_REGIONKEY LIMIT 10, 20", 423 "SELECT N_NAME, N_REGIONKEY a FROM NATION WHERE N_REGIONKEY > 0 ORDER BY a DESC", //test alias 424 "SELECT NATION.N_NAME FROM NATION", //test alias 425 "SELECT * FROM NATION", //test star 426 "SELECT a.* FROM NATION a", //test star 427 "SELECT count(*) FROM NATION", //test star 428 "SELECT count(*) FROM NATION group by N_NAME", //test star 429 "SELECT N_NAME, count(distinct N_REGIONKEY) FROM NATION group by N_NAME", //test distinct agg function 430 "SELECT N_NAME, MAX(N_REGIONKEY) FROM NATION GROUP BY N_NAME HAVING MAX(N_REGIONKEY) > 10", //test agg 431 "SELECT DISTINCT N_NAME FROM NATION", //test distinct 432 "select sum(n_nationkey) as s from nation order by s", 433 "select date_add(date '2001-01-01', interval 1 day) as a", 434 "select date_sub(date '2001-01-01', interval '1' day) as a", 435 "select date_add('2001-01-01', interval '1' day) as a", 436 "select n_name, count(*) from nation group by n_name order by 2 asc", 437 "select count(distinct 12)", 438 "select nullif(n_name, n_comment), ifnull(n_comment, n_name) from nation", 439 440 "select 18446744073709551500", 441 "select 0xffffffffffffffff", 442 "select 0xffff", 443 444 "SELECT N_REGIONKEY + 2 as a, N_REGIONKEY/2, N_REGIONKEY* N_NATIONKEY, N_REGIONKEY % N_NATIONKEY, N_REGIONKEY - N_NATIONKEY FROM NATION WHERE -N_NATIONKEY < -20", //test more expr 445 "SELECT N_REGIONKEY FROM NATION where N_REGIONKEY >= N_NATIONKEY or (N_NAME like '%ddd' and N_REGIONKEY >0.5)", //test more expr 446 "SELECT N_REGIONKEY FROM NATION where N_REGIONKEY between 2 and 2 OR N_NATIONKEY not between 3 and 10", //test more expr 447 // "SELECT N_REGIONKEY FROM NATION where N_REGIONKEY is null and N_NAME is not null", 448 "SELECT N_REGIONKEY FROM NATION where N_REGIONKEY IN (1, 2)", //test more expr 449 "SELECT N_REGIONKEY FROM NATION where N_REGIONKEY NOT IN (1)", //test more expr 450 "select N_REGIONKEY from nation group by N_REGIONKEY having abs(nation.N_REGIONKEY - 1) >10", 451 452 "SELECT -1", 453 "select date_add('1997-12-31 23:59:59',INTERVAL 100000 SECOND)", 454 "select date_sub('1997-12-31 23:59:59',INTERVAL 2 HOUR)", 455 "select @str_var, @int_var, @bool_var, @float_var, @null_var", 456 "select @str_var, @@global.int_var, @@session.bool_var", 457 "select n_name from nation where n_name != @str_var and n_regionkey > @int_var", 458 "select n_name from nation where n_name != @@global.str_var and n_regionkey > @@session.int_var", 459 "select distinct(n_name), ((abs(n_regionkey))) from nation", 460 "SET @var = abs(-1), @@session.string_var = 'aaa'", 461 "SET NAMES 'utf8mb4' COLLATE 'utf8mb4_general_ci'", 462 "SELECT DISTINCT N_NAME FROM NATION ORDER BY N_NAME", //test distinct with order by 463 464 "prepare stmt1 from select * from nation", 465 "prepare stmt1 from select * from nation where n_name = ?", 466 "prepare stmt1 from 'select * from nation where n_name = ?'", 467 "prepare stmt1 from 'insert into nation select * from nation2 where n_name = ?'", 468 "prepare stmt1 from 'select * from nation where n_name = ?'", 469 "prepare stmt1 from 'drop table if exists t1'", 470 "prepare stmt1 from 'create table t1 (a int)'", 471 "prepare stmt1 from select N_REGIONKEY from nation group by N_REGIONKEY having abs(nation.N_REGIONKEY - ?) > ?", 472 "execute stmt1", 473 "execute stmt1 using @str_var, @@global.int_var", 474 "deallocate prepare stmt1", 475 "drop prepare stmt1", 476 "select count(n_name) from nation limit 10", 477 "select l_shipdate + interval '1' day from lineitem", 478 "select interval '1' day + l_shipdate from lineitem", 479 "select interval '1' day + cast('2022-02-02 00:00:00' as datetime)", 480 "select cast('2022-02-02 00:00:00' as datetime) + interval '1' day", 481 "select true is unknown", 482 "select null is not unknown", 483 "select 1 as c, 1/2, abs(-2)", 484 485 "select date('2022-01-01'), adddate(time'00:00:00', interval 1 day), subdate(time'00:00:00', interval 1 week), '2007-01-01' + interval 1 month, '2007-01-01' - interval 1 hour", 486 "select 2222332222222223333333333333333333, 0x616263,-10, bit_and(2), bit_or(2), bit_xor(10.1), 'aaa' like '%a',str_to_date('04/31/2004', '%m/%d/%Y'),unix_timestamp(from_unixtime(2147483647))", 487 "select max(n_nationkey) over (partition by N_REGIONKEY) from nation", 488 "select * from generate_series(1, 5) g", 489 "prepare stmt1 from select * from nation where n_name like ? or n_nationkey > 10 order by 2 limit '10'", 490 491 "values row(1,1), row(2,2), row(3,3) order by column_0 limit 2", 492 "select * from (values row(1,1), row(2,2), row(3,3)) a (c1, c2)", 493 "prepare stmt1 from select * from nation where n_name like ? or n_nationkey > 10 order by 2 limit '10' for update", 494 } 495 runTestShouldPass(mock, t, sqls, false, false) 496 497 // should error 498 sqls = []string{ 499 "SELECT N_NAME, N_REGIONKEY FROM table_not_exist", //table not exist 500 "SELECT N_NAME, column_not_exist FROM NATION", //column not exist 501 "SELECT N_NAME, N_REGIONKEY a FROM NATION ORDER BY cccc", //column alias not exist 502 "SELECT N_NAME, b.N_REGIONKEY FROM NATION a ORDER BY b.N_REGIONKEY", //table alias not exist 503 "SELECT N_NAME FROM NATION WHERE ffff(N_REGIONKEY) > 0", //function name not exist 504 "SELECT NATION.N_NAME FROM NATION a", // mysql should error, but i don't think it is necesssary 505 "select n_nationkey, sum(n_nationkey) from nation", 506 "SET @var = abs(a)", // can't use column 507 "SET @var = avg(2)", // can't use agg function 508 509 "SELECT DISTINCT N_NAME FROM NATION GROUP BY N_REGIONKEY", //test distinct with group by 510 "SELECT DISTINCT N_NAME FROM NATION ORDER BY N_REGIONKEY", //test distinct with order by 511 "select count(n_name) from nation limit 10 for update", 512 //"select 18446744073709551500", //over int64 513 //"select 0xffffffffffffffff", //over int64 514 } 515 runTestShouldError(mock, t, sqls) 516 } 517 518 // test join table plan building 519 func TestJoinTableSqlBuilder(t *testing.T) { 520 mock := NewMockOptimizer(false) 521 522 // should pass 523 sqls := []string{ 524 "SELECT N_NAME,N_REGIONKEY FROM NATION join REGION on NATION.N_REGIONKEY = REGION.R_REGIONKEY", 525 "SELECT N_NAME, N_REGIONKEY FROM NATION join REGION on NATION.N_REGIONKEY = REGION.R_REGIONKEY WHERE NATION.N_REGIONKEY > 0", 526 "SELECT N_NAME, NATION2.R_REGIONKEY FROM NATION2 join REGION using(R_REGIONKEY) WHERE NATION2.R_REGIONKEY > 0", 527 "SELECT N_NAME, NATION2.R_REGIONKEY FROM NATION2 NATURAL JOIN REGION WHERE NATION2.R_REGIONKEY > 0", 528 "SELECT N_NAME FROM NATION NATURAL JOIN REGION", //have no same column name but it's ok 529 "SELECT N_NAME,N_REGIONKEY FROM NATION a join REGION b on a.N_REGIONKEY = b.R_REGIONKEY WHERE a.N_REGIONKEY > 0", //test alias 530 "SELECT l.L_ORDERKEY a FROM CUSTOMER c, ORDERS o, LINEITEM l WHERE c.C_CUSTKEY = o.O_CUSTKEY and l.L_ORDERKEY = o.O_ORDERKEY and o.O_ORDERKEY < 10", //join three tables 531 "SELECT c.* FROM CUSTOMER c, ORDERS o, LINEITEM l WHERE c.C_CUSTKEY = o.O_CUSTKEY and l.L_ORDERKEY = o.O_ORDERKEY", //test star 532 "SELECT * FROM CUSTOMER c, ORDERS o, LINEITEM l WHERE c.C_CUSTKEY = o.O_CUSTKEY and l.L_ORDERKEY = o.O_ORDERKEY", //test star 533 "SELECT a.* FROM NATION a join REGION b on a.N_REGIONKEY = b.R_REGIONKEY WHERE a.N_REGIONKEY > 0", //test star 534 "SELECT * FROM NATION a join REGION b on a.N_REGIONKEY = b.R_REGIONKEY WHERE a.N_REGIONKEY > 0", 535 "SELECT N_NAME, R_REGIONKEY FROM NATION2 join REGION using(R_REGIONKEY)", 536 "select nation.n_name from nation join nation2 on nation.n_name !='a' join region on nation.n_regionkey = region.r_regionkey", 537 "select * from nation, nation2, region", 538 } 539 runTestShouldPass(mock, t, sqls, false, false) 540 541 // should error 542 sqls = []string{ 543 "SELECT N_NAME,N_REGIONKEY FROM NATION join REGION on NATION.N_REGIONKEY = REGION.NotExistColumn", //column not exist 544 "SELECT N_NAME, R_REGIONKEY FROM NATION join REGION using(R_REGIONKEY)", //column not exist 545 "SELECT N_NAME,N_REGIONKEY FROM NATION a join REGION b on a.N_REGIONKEY = b.R_REGIONKEY WHERE aaaaa.N_REGIONKEY > 0", //table alias not exist 546 "select *", //No table used 547 "SELECT * FROM NATION a join REGION b on a.N_REGIONKEY = b.R_REGIONKEY WHERE a.N_REGIONKEY > 0 for update", //Not support 548 "select * from nation, nation2, region for update", // Not support 549 } 550 runTestShouldError(mock, t, sqls) 551 } 552 553 // test derived table plan building 554 func TestDerivedTableSqlBuilder(t *testing.T) { 555 mock := NewMockOptimizer(false) 556 // should pass 557 sqls := []string{ 558 "select c_custkey from (select c_custkey from CUSTOMER ) a", 559 "select c_custkey from (select c_custkey from CUSTOMER group by c_custkey ) a", 560 "select col1 from (select c_custkey from CUSTOMER group by c_custkey ) a(col1)", 561 "select c_custkey from (select c_custkey, count(C_NATIONKEY) ff from CUSTOMER group by c_custkey ) a where ff > 0 order by c_custkey", 562 "select col1 from (select c_custkey, count(C_NATIONKEY) ff from CUSTOMER group by c_custkey ) a(col1, col2) where col2 > 0 order by col1", 563 "select c_custkey from (select c_custkey, count(C_NATIONKEY) ff from CUSTOMER group by c_custkey ) a join NATION b on a.c_custkey = b.N_REGIONKEY where b.N_NATIONKEY > 10", 564 "select a.* from (select c_custkey, count(C_NATIONKEY) ff from CUSTOMER group by c_custkey ) a join NATION b on a.c_custkey = b.N_REGIONKEY where b.N_NATIONKEY > 10", 565 "select * from (select c_custkey, count(C_NATIONKEY) ff from CUSTOMER group by c_custkey ) a join NATION b on a.c_custkey = b.N_REGIONKEY where b.N_NATIONKEY > 10", 566 } 567 runTestShouldPass(mock, t, sqls, false, false) 568 569 // should error 570 sqls = []string{ 571 "select C_NAME from (select c_custkey from CUSTOMER) a", //column not exist 572 "select c_custkey2222 from (select c_custkey from CUSTOMER group by c_custkey ) a", //column not exist 573 "select col1 from (select c_custkey from CUSTOMER group by c_custkey ) a(col1, col2)", //column length not match 574 "select c_custkey from (select c_custkey from CUSTOMER group by c_custkey) a(col1)", //column not exist 575 "select c_custkey from (select c_custkey from CUSTOMER ) a for update ", //not support 576 } 577 runTestShouldError(mock, t, sqls) 578 } 579 580 // test derived table plan building 581 func TestUnionSqlBuilder(t *testing.T) { 582 mock := NewMockOptimizer(false) 583 // should pass 584 sqls := []string{ 585 "(select 1) union (select 1)", 586 "(((select n_nationkey from nation order by n_nationkey))) union (((select n_nationkey from nation order by n_nationkey)))", 587 "select 1 union select 2", 588 "select 1 union (select 2 union select 3)", 589 "(select 1 union select 2) union select 3 intersect select 4 order by 1", 590 "select 1 union select null", 591 "select n_name from nation intersect select n_name from nation2", 592 "select n_name from nation minus select n_name from nation2", 593 "select 1 union select 2 intersect select 2 union all select 1.1 minus select 22222", 594 "select 1 as a union select 2 order by a limit 1", 595 "select n_name from nation union select n_comment from nation order by n_name", 596 "with qn (foo, bar) as (select 1 as col, 2 as coll union select 4, 5) select qn1.bar from qn qn1", 597 "select n_name, n_comment from nation union all select n_name, n_comment from nation2", 598 "select n_name from nation intersect all select n_name from nation2", 599 } 600 runTestShouldPass(mock, t, sqls, false, false) 601 602 // should error 603 sqls = []string{ 604 "select 1 union select 2, 'a'", 605 "select n_name as a from nation union select n_comment from nation order by n_name", 606 "select n_name from nation minus all select n_name from nation2", // not support 607 } 608 runTestShouldError(mock, t, sqls) 609 } 610 611 // test CTE plan building 612 func TestCTESqlBuilder(t *testing.T) { 613 mock := NewMockOptimizer(false) 614 615 // should pass 616 sqls := []string{ 617 "WITH qn AS (SELECT * FROM nation) SELECT * FROM qn;", 618 "with qn0 as (select 1), qn1 as (select * from qn0), qn2 as (select 1), qn3 as (select 1 from qn1, qn2) select 1 from qn3", 619 620 `WITH qn AS (select "outer" as a) 621 SELECT (WITH qn AS (SELECT "inner" as a) SELECT a from qn), 622 qn.a 623 FROM qn`, 624 } 625 runTestShouldPass(mock, t, sqls, false, false) 626 627 // should error 628 sqls = []string{ 629 "WITH qn(a, b) AS (SELECT * FROM nation) SELECT * FROM qn;", 630 `with qn1 as (with qn3 as (select * from qn2) select * from qn3), 631 qn2 as (select 1) 632 select * from qn1`, 633 634 `WITH qn2 AS (SELECT a FROM qn WHERE a IS NULL or a>0), 635 qn AS (SELECT b as a FROM qn2) 636 SELECT qn.a FROM qn`, 637 } 638 runTestShouldError(mock, t, sqls) 639 } 640 641 func TestInsert(t *testing.T) { 642 mock := NewMockOptimizer(false) 643 // should pass 644 sqls := []string{ 645 "INSERT INTO NATION VALUES (1, 'NAME1',21, 'COMMENT1'), (2, 'NAME2', 22, 'COMMENT2')", 646 "INSERT INTO NATION (N_NATIONKEY, N_REGIONKEY, N_NAME, N_COMMENT) VALUES (1, 21, 'NAME1','comment1'), (2, 22, 'NAME2', 'comment2')", 647 "INSERT INTO NATION SELECT * FROM NATION2", 648 } 649 runTestShouldPass(mock, t, sqls, false, false) 650 651 // should error 652 sqls = []string{ 653 "INSERT NATION VALUES (1, 'NAME1',21, 'COMMENT1'), ('NAME2', 22, 'COMMENT2')", // doesn't match value count 654 "INSERT NATION (N_NATIONKEY, N_REGIONKEY, N_NAME) VALUES (1, 'NAME1'), (2, 22, 'NAME2')", // doesn't match value count 655 "INSERT NATION (N_NATIONKEY, N_REGIONKEY, N_NAME2222) VALUES (1, 21, 'NAME1'), (2, 22, 'NAME2')", // column not exist 656 "INSERT NATION333 (N_NATIONKEY, N_REGIONKEY, N_NAME2222) VALUES (1, 2, 'NAME1'), (2, 22, 'NAME2')", // table not exist 657 "INSERT NATION (N_NATIONKEY, N_REGIONKEY, N_NAME2222) VALUES (1, 'should int32', 'NAME1'), (2, 22, 'NAME2')", // column type not match 658 "INSERT NATION (N_NATIONKEY, N_REGIONKEY, N_NAME2222) VALUES (1, 2.22, 'NAME1'), (2, 22, 'NAME2')", // column type not match 659 "INSERT NATION (N_NATIONKEY, N_REGIONKEY, N_NAME2222) VALUES (1, 2, 'NAME1'), (2, 22, 'NAME2')", // function expr not support now 660 "INSERT INTO region SELECT * FROM NATION2", // column length not match 661 "INSERT INTO region SELECT 1, 2, 3, 4, 5, 6 FROM NATION2", // column length not match 662 "INSERT NATION333 (N_NATIONKEY, N_REGIONKEY, N_NAME2222) SELECT 1, 2, 3 FROM NATION2", // table not exist 663 } 664 runTestShouldError(mock, t, sqls) 665 } 666 667 func TestUpdate(t *testing.T) { 668 mock := NewMockOptimizer(true) 669 // should pass 670 sqls := []string{ 671 "UPDATE NATION SET N_NAME ='U1', N_REGIONKEY=2", 672 "UPDATE NATION SET N_NAME ='U1', N_REGIONKEY=2 WHERE N_NATIONKEY > 10 LIMIT 20", 673 "UPDATE NATION SET N_NAME ='U1', N_REGIONKEY=N_REGIONKEY+2 WHERE N_NATIONKEY > 10 LIMIT 20", 674 "update NATION a join NATION2 b on a.N_REGIONKEY = b.R_REGIONKEY set a.N_NAME = 'aa'", 675 "prepare stmt1 from 'update nation set n_name = ? where n_nationkey > ?'", 676 "drop index idx1 on test_idx", 677 } 678 runTestShouldPass(mock, t, sqls, false, false) 679 680 // should error 681 sqls = []string{ 682 "UPDATE NATION SET N_NAME2 ='U1', N_REGIONKEY=2", // column not exist 683 "UPDATE NATION2222 SET N_NAME ='U1', N_REGIONKEY=2", // table not exist 684 } 685 runTestShouldError(mock, t, sqls) 686 } 687 688 func TestDelete(t *testing.T) { 689 mock := NewMockOptimizer(true) 690 // should pass 691 sqls := []string{ 692 "DELETE FROM NATION", 693 "DELETE FROM NATION WHERE N_NATIONKEY > 10", 694 "DELETE FROM NATION WHERE N_NATIONKEY > 10 LIMIT 20", 695 "delete nation from nation left join nation2 on nation.n_nationkey = nation2.n_nationkey", 696 "delete from nation", 697 "delete nation, nation2 from nation join nation2 on nation.n_name = nation2.n_name", 698 "prepare stmt1 from 'delete from nation where n_nationkey > ?'", 699 } 700 runTestShouldPass(mock, t, sqls, false, false) 701 702 // should error 703 sqls = []string{ 704 "DELETE FROM NATION2222", // table not exist 705 "DELETE FROM NATION WHERE N_NATIONKEY2 > 10", // column not found 706 } 707 runTestShouldError(mock, t, sqls) 708 } 709 710 func TestSubQuery(t *testing.T) { 711 mock := NewMockOptimizer(false) 712 // should pass 713 sqls := []string{ 714 "SELECT * FROM NATION where N_REGIONKEY > (select max(R_REGIONKEY) from REGION)", // unrelated 715 "SELECT * FROM NATION where N_REGIONKEY in (select max(R_REGIONKEY) from REGION)", // unrelated 716 "SELECT * FROM NATION where N_REGIONKEY not in (select max(R_REGIONKEY) from REGION)", // unrelated 717 "SELECT * FROM NATION where exists (select max(R_REGIONKEY) from REGION)", // unrelated 718 "SELECT * FROM NATION where N_REGIONKEY > (select max(R_REGIONKEY) from REGION where R_REGIONKEY = N_REGIONKEY)", // related 719 //"DELETE FROM NATION WHERE N_NATIONKEY > 10", 720 `select 721 sum(l_extendedprice) / 7.0 as avg_yearly 722 from 723 lineitem, 724 part 725 where 726 p_partkey = l_partkey 727 and p_brand = 'Brand#54' 728 and p_container = 'LG BAG' 729 and l_quantity < ( 730 select 731 0.2 * avg(l_quantity) 732 from 733 lineitem 734 where 735 l_partkey = p_partkey 736 );`, //tpch q17 737 "select * from nation where n_regionkey in (select r_regionkey from region) and n_nationkey not in (1,2) and n_nationkey = some (select n_nationkey from nation2)", 738 } 739 runTestShouldPass(mock, t, sqls, false, false) 740 741 // should error 742 sqls = []string{ 743 "SELECT * FROM NATION where N_REGIONKEY > (select max(R_REGIONKEY) from REGION222)", // table not exist 744 "SELECT * FROM NATION where N_REGIONKEY > (select max(R_REGIONKEY) from REGION where R_REGIONKEY < N_REGIONKEY222)", // column not exist 745 "SELECT * FROM NATION where N_REGIONKEY > (select max(R_REGIONKEY) from REGION where R_REGIONKEY < N_REGIONKEY)", // related 746 "SELECT * FROM NATION where N_REGIONKEY > (select max(R_REGIONKEY) from REGION) for update", // not support 747 } 748 runTestShouldError(mock, t, sqls) 749 } 750 751 func TestMysqlCompatibilityMode(t *testing.T) { 752 mock := NewMockOptimizer(false) 753 754 sqls := []string{ 755 "SELECT n_nationkey FROM NATION group by n_name", 756 "SELECT n_nationkey, min(n_name) FROM NATION", 757 "SELECT n_nationkey + 100 FROM NATION group by n_name", 758 } 759 // withou mysql compatibility 760 runTestShouldError(mock, t, sqls) 761 // with mysql compatibility 762 mock.ctxt.mysqlCompatible = true 763 runTestShouldPass(mock, t, sqls, false, false) 764 } 765 766 func TestTcl(t *testing.T) { 767 mock := NewMockOptimizer(false) 768 // should pass 769 sqls := []string{ 770 "start transaction", 771 "start transaction read write", 772 "begin", 773 "commit and chain", 774 "commit and chain no release", 775 "rollback and chain", 776 } 777 runTestShouldPass(mock, t, sqls, false, false) 778 779 // should error 780 sqls = []string{} 781 runTestShouldError(mock, t, sqls) 782 } 783 784 func TestDdl(t *testing.T) { 785 mock := NewMockOptimizer(true) 786 // should pass 787 sqls := []string{ 788 "create database db_name", //db not exists and pass 789 "create database if not exists db_name", //db not exists but pass 790 "create database if not exists tpch", //db exists and pass 791 "drop database if exists db_name", //db not exists but pass 792 "drop database tpch", //db exists, pass 793 "create view v1 as select * from nation", 794 795 "create table tbl_name (t bool(20) comment 'dd', b int unsigned, c char(20), d varchar(20), primary key(b), index idx_t(c)) comment 'test comment'", 796 "create table if not exists tbl_name (b int default 20 primary key, c char(20) default 'ss', d varchar(20) default 'kkk')", 797 "create table if not exists nation (t bool(20), b int, c char(20), d varchar(20))", 798 "drop table if exists tbl_name", 799 "drop table if exists nation", 800 "drop table nation", 801 "drop table tpch.nation", 802 "drop table if exists tpch.tbl_not_exist", 803 "drop table if exists db_not_exist.tbl", 804 "drop view v1", 805 "truncate nation", 806 "truncate tpch.nation", 807 "truncate table nation", 808 "truncate table tpch.nation", 809 "create unique index idx_name on nation(n_regionkey)", 810 "create view v_nation as select n_nationkey,n_name,n_regionkey,n_comment from nation", 811 "CREATE TABLE t1(id INT PRIMARY KEY,name VARCHAR(25),deptId INT,CONSTRAINT fk_t1 FOREIGN KEY(deptId) REFERENCES nation(n_nationkey)) COMMENT='xxxxx'", 812 "create table t2(empno int unsigned,ename varchar(15),job varchar(10)) cluster by(empno,ename)", 813 "lock tables nation read", 814 "lock tables nation write, supplier read", 815 "unlock tables", 816 "alter table emp drop foreign key fk1", 817 "alter table nation add FOREIGN KEY fk_t1(n_nationkey) REFERENCES nation2(n_nationkey)", 818 } 819 runTestShouldPass(mock, t, sqls, false, false) 820 821 // should error 822 sqls = []string{ 823 // "create database tpch", // check in pipeline now 824 // "drop database db_name", // check in pipeline now 825 // "create table nation (t bool(20), b int, c char(20), d varchar(20))", // check in pipeline now 826 "create table nation (b int primary key, c char(20) primary key, d varchar(20))", //Multiple primary key 827 "drop table tbl_name", //table not exists in tpch 828 "drop table tpch.tbl_not_exist", //database not exists 829 "drop table db_not_exist.tbl", //table not exists 830 "create table t6(empno int unsigned,ename varchar(15) auto_increment) cluster by(empno,ename)", 831 "lock tables t3 read", 832 "lock tables t1 read, t1 write", 833 "lock tables nation read, nation write", 834 "alter table nation drop foreign key fk1", //key not exists 835 "alter table nation add FOREIGN KEY fk_t1(col_not_exist) REFERENCES nation2(n_nationkey)", 836 "alter table nation add FOREIGN KEY fk_t1(n_nationkey) REFERENCES nation2(col_not_exist)", 837 "create table agg01 (col1 int, col2 enum('egwjqebwq', 'qwewqewqeqewq', 'weueiwqeowqehwgqjhenw') primary key)", 838 } 839 runTestShouldError(mock, t, sqls) 840 } 841 842 func TestShow(t *testing.T) { 843 mock := NewMockOptimizer(false) 844 // should pass 845 sqls := []string{ 846 "show variables", 847 //"show create database tpch", 848 "show create table nation", 849 "show create table tpch.nation", 850 "show databases", 851 "show databases like '%d'", 852 "show databases where `database` = '11'", 853 "show databases where `database` = '11' or `database` = 'ddd'", 854 "show tables", 855 "show tables from tpch", 856 "show tables like '%dd'", 857 "show tables from tpch where `tables_in_tpch` = 'aa' or `tables_in_tpch` like '%dd'", 858 "show columns from nation", 859 "show full columns from nation", 860 "show columns from nation from tpch", 861 "show full columns from nation from tpch", 862 "show columns from nation where `field` like '%ff' or `type` = 1 or `null` = 0", 863 "show full columns from nation where `field` like '%ff' or `type` = 1 or `null` = 0", 864 "show create view v1", 865 "show create table v1", 866 "show table_number", 867 "show table_number from tpch", 868 "show column_number from nation", 869 "show config", 870 "show index from tpch.nation", 871 "show locks", 872 "show node list", 873 "show grants for ROLE role1", 874 "show function status", 875 "show function status like '%ff'", 876 "show snapshots", 877 "show snapshots where SNAPSHOT_NAME = 'snapshot_07'", 878 // "show procedure status", 879 // "show procedure status like '%ff'", 880 "show roles", 881 "show roles like '%ff'", 882 "show stages", 883 "show stages like 'my_stage%'", 884 // "show grants", 885 } 886 runTestShouldPass(mock, t, sqls, false, false) 887 888 // should error 889 sqls = []string{ 890 "show create database db_not_exist", //db no exist 891 "show create table tpch.nation22", //table not exist 892 "show create view vvv", //view not exist 893 "show databases where d ='a'", //Column not exist, show databases only have one column named 'Database' 894 "show databases where `Databaseddddd` = '11'", //column not exist 895 "show tables from tpch22222", //database not exist 896 "show tables from tpch where Tables_in_tpch222 = 'aa'", //column not exist 897 "show columns from nation_ddddd", //table not exist 898 "show full columns from nation_ddddd", 899 "show columns from nation_ddddd from tpch", //table not exist 900 "show full columns from nation_ddddd from tpch", 901 "show columns from nation where `Field22` like '%ff'", //column not exist 902 "show full columns from nation where `Field22` like '%ff'", 903 "show index from tpch.dddd", 904 "show table_number from tpch222", 905 "show column_number from nation222", 906 } 907 runTestShouldError(mock, t, sqls) 908 } 909 910 func TestResultColumns(t *testing.T) { 911 mock := NewMockOptimizer(false) 912 getColumns := func(sql string) []*ColDef { 913 logicPlan, err := runOneStmt(mock, t, sql) 914 if err != nil { 915 t.Fatalf("sql %s build plan error:%+v", sql, err) 916 } 917 return GetResultColumnsFromPlan(logicPlan) 918 } 919 920 returnNilSQL := []string{ 921 "begin", 922 "commit", 923 "rollback", 924 "INSERT NATION VALUES (1, 'NAME1',21, 'COMMENT1'), (2, 'NAME2', 22, 'COMMENT2')", 925 // "UPDATE NATION SET N_NAME ='U1', N_REGIONKEY=2", 926 // "DELETE FROM NATION", 927 //"create database db_name", 928 //"drop database tpch", 929 //"create table tbl_name (b int unsigned, c char(20))", 930 //"drop table nation", 931 } 932 for _, sql := range returnNilSQL { 933 columns := getColumns(sql) 934 if columns != nil { 935 t.Fatalf("sql:%+v, return columns should be nil", sql) 936 } 937 } 938 939 returnColumnsSQL := map[string]string{ 940 "SELECT N_NAME, N_REGIONKEY a FROM NATION WHERE N_REGIONKEY > 0 ORDER BY a DESC": "N_NAME,a", 941 "select n_nationkey, sum(n_regionkey) from (select * from nation) sub group by n_nationkey": "n_nationkey,sum(n_regionkey)", 942 "show variables": "Variable_name,Value", 943 "show create database tpch": "Database,Create Database", 944 "show create table nation": "Table,Create Table", 945 "show databases": "Database", 946 "show tables": "Tables_in_tpch", 947 "show columns from nation": "Field,Type,Null,Key,Default,Extra,Comment", 948 } 949 for sql, colsStr := range returnColumnsSQL { 950 cols := strings.Split(colsStr, ",") 951 columns := getColumns(sql) 952 if len(columns) != len(cols) { 953 t.Fatalf("sql:%+v, return columns should be [%s]", sql, colsStr) 954 } 955 for idx, col := range cols { 956 // now ast always change col_name to lower string. will be fixed soon 957 if !strings.EqualFold(columns[idx].Name, col) { 958 t.Fatalf("sql:%+v, return columns should be [%s]", sql, colsStr) 959 } 960 } 961 } 962 } 963 964 func TestResultColumns2(t *testing.T) { 965 mock := NewMockOptimizer(true) 966 getColumns := func(sql string) []*ColDef { 967 logicPlan, err := runOneStmt(mock, t, sql) 968 if err != nil { 969 t.Fatalf("sql %s build plan error:%+v", sql, err) 970 } 971 return GetResultColumnsFromPlan(logicPlan) 972 } 973 974 returnNilSQL := []string{ 975 "create database db_name", 976 "drop database tpch", 977 "create table tbl_name (b int unsigned, c char(20))", 978 "drop table nation", 979 } 980 for _, sql := range returnNilSQL { 981 columns := getColumns(sql) 982 if columns != nil { 983 t.Fatalf("sql:%+v, return columns should be nil", sql) 984 } 985 } 986 987 returnColumnsSQL := map[string]string{ 988 "SELECT N_NAME, N_REGIONKEY a FROM NATION WHERE N_REGIONKEY > 0 ORDER BY a DESC": "N_NAME,a", 989 "select n_nationkey, sum(n_regionkey) from (select * from nation) sub group by n_nationkey": "n_nationkey,sum(n_regionkey)", 990 "show variables": "Variable_name,Value", 991 "show create database tpch": "Database,Create Database", 992 "show create table nation": "Table,Create Table", 993 "show databases": "Database", 994 "show tables": "Tables_in_tpch", 995 "show columns from nation": "Field,Type,Null,Key,Default,Extra,Comment", 996 } 997 for sql, colsStr := range returnColumnsSQL { 998 cols := strings.Split(colsStr, ",") 999 columns := getColumns(sql) 1000 if len(columns) != len(cols) { 1001 t.Fatalf("sql:%+v, return columns should be [%s]", sql, colsStr) 1002 } 1003 for idx, col := range cols { 1004 // now ast always change col_name to lower string. will be fixed soon 1005 if !strings.EqualFold(columns[idx].Name, col) { 1006 t.Fatalf("sql:%+v, return columns should be [%s]", sql, colsStr) 1007 } 1008 } 1009 } 1010 } 1011 1012 func TestBuildUnnest(t *testing.T) { 1013 mock := NewMockOptimizer(false) 1014 sqls := []string{ 1015 `select * from unnest('{"a":1}') as f`, 1016 `select * from unnest('{"a":1}', '') as f`, 1017 `select * from unnest('{"a":1}', '$', true) as f`, 1018 } 1019 runTestShouldPass(mock, t, sqls, false, false) 1020 errSqls := []string{ 1021 `select * from unnest(t.t1.a)`, 1022 `select * from unnest(t.a, "$.b")`, 1023 `select * from unnest(t.a, "$.b", true)`, 1024 `select * from unnest(t.a) as f`, 1025 `select * from unnest(t.a, "$.b") as f`, 1026 `select * from unnest(t.a, "$.b", true) as f`, 1027 `select * from unnest('{"a":1}')`, 1028 `select * from unnest('{"a":1}', "$")`, 1029 `select * from unnest('{"a":1}', "", true)`, 1030 } 1031 runTestShouldError(mock, t, errSqls) 1032 } 1033 1034 func TestVisitRule(t *testing.T) { 1035 sql := "select * from nation where n_nationkey > 10 or n_nationkey=@int_var or abs(-1) > 1" 1036 mock := NewMockOptimizer(false) 1037 ctx := context.TODO() 1038 plan, err := runOneStmt(mock, t, sql) 1039 if err != nil { 1040 t.Fatalf("should not error, sql=%s", sql) 1041 } 1042 getParamRule := NewGetParamRule() 1043 vp := NewVisitPlan(plan, []VisitPlanRule{getParamRule}) 1044 err = vp.Visit(context.TODO()) 1045 if err != nil { 1046 t.Fatalf("should not error, sql=%s", sql) 1047 } 1048 getParamRule.SetParamOrder() 1049 args := getParamRule.params 1050 1051 resetParamOrderRule := NewResetParamOrderRule(args) 1052 vp = NewVisitPlan(plan, []VisitPlanRule{resetParamOrderRule}) 1053 err = vp.Visit(ctx) 1054 if err != nil { 1055 t.Fatalf("should not error, sql=%s", sql) 1056 } 1057 1058 params := []*Expr{ 1059 makePlan2Int64ConstExprWithType(10), 1060 } 1061 resetParamRule := NewResetParamRefRule(ctx, params) 1062 resetVarRule := NewResetVarRefRule(&mock.ctxt, testutil.NewProc()) 1063 constantFoldRule := NewConstantFoldRule(&mock.ctxt) 1064 vp = NewVisitPlan(plan, []VisitPlanRule{resetParamRule, resetVarRule, constantFoldRule}) 1065 err = vp.Visit(ctx) 1066 if err != nil { 1067 t.Fatalf("should not error, sql=%s", sql) 1068 } 1069 } 1070 1071 func getJSON(v any, t *testing.T) []byte { 1072 b, err := json.Marshal(v) 1073 if err != nil { 1074 t.Logf("%+v", v) 1075 } 1076 var out bytes.Buffer 1077 err = json.Indent(&out, b, "", " ") 1078 if err != nil { 1079 t.Logf("%+v", v) 1080 } 1081 return out.Bytes() 1082 } 1083 1084 func testDeepCopy(logicPlan *Plan) { 1085 switch logicPlan.Plan.(type) { 1086 case *plan.Plan_Query: 1087 _ = DeepCopyPlan(logicPlan) 1088 case *plan.Plan_Ddl: 1089 _ = DeepCopyPlan(logicPlan) 1090 case *plan.Plan_Dcl: 1091 } 1092 } 1093 1094 func outPutPlan(logicPlan *Plan, toFile bool, t *testing.T) { 1095 var json []byte 1096 switch logicPlan.Plan.(type) { 1097 case *plan.Plan_Query: 1098 json = getJSON(logicPlan.GetQuery(), t) 1099 case *plan.Plan_Tcl: 1100 json = getJSON(logicPlan.GetTcl(), t) 1101 case *plan.Plan_Ddl: 1102 json = getJSON(logicPlan.GetDdl(), t) 1103 case *plan.Plan_Dcl: 1104 json = getJSON(logicPlan.GetDcl(), t) 1105 } 1106 if toFile { 1107 err := os.WriteFile("/tmp/mo_plan_test.json", json, 0777) 1108 if err != nil { 1109 t.Logf("%+v", err) 1110 } 1111 } else { 1112 t.Logf(string(json)) 1113 } 1114 } 1115 1116 func runOneStmt(opt Optimizer, t *testing.T, sql string) (*Plan, error) { 1117 stmts, err := mysql.Parse(opt.CurrentContext().GetContext(), sql, 1, 0) 1118 if err != nil { 1119 t.Fatalf("%+v", err) 1120 } 1121 // this sql always return one stmt 1122 ctx := opt.CurrentContext() 1123 return BuildPlan(ctx, stmts[0], false) 1124 } 1125 1126 func runTestShouldPass(opt Optimizer, t *testing.T, sqls []string, printJSON bool, toFile bool) { 1127 for _, sql := range sqls { 1128 logicPlan, err := runOneStmt(opt, t, sql) 1129 if err != nil { 1130 t.Fatalf("%+v, sql=%v", err, sql) 1131 } 1132 testDeepCopy(logicPlan) 1133 if printJSON { 1134 outPutPlan(logicPlan, toFile, t) 1135 } 1136 } 1137 } 1138 1139 func runTestShouldError(opt Optimizer, t *testing.T, sqls []string) { 1140 for _, sql := range sqls { 1141 _, err := runOneStmt(opt, t, sql) 1142 if err == nil { 1143 t.Fatalf("should error, but pass: %v", sql) 1144 } 1145 } 1146 } 1147 1148 func Test_mergeContexts(t *testing.T) { 1149 b1 := NewBinding(0, 1, "db", "a", 0, nil, nil, nil, false, nil) 1150 bc1 := NewBindContext(nil, nil) 1151 bc1.bindings = append(bc1.bindings, b1) 1152 1153 b2 := NewBinding(1, 2, "db", "a", 0, nil, nil, nil, false, nil) 1154 bc2 := NewBindContext(nil, nil) 1155 bc2.bindings = append(bc2.bindings, b2) 1156 1157 bc := NewBindContext(nil, nil) 1158 1159 //a merge a 1160 err := bc.mergeContexts(context.Background(), bc1, bc2) 1161 assert.Error(t, err) 1162 assert.EqualError(t, err, "invalid input: table 'a' specified more than once") 1163 1164 //a merge b 1165 b3 := NewBinding(2, 3, "db", "b", 0, nil, nil, nil, false, nil) 1166 bc3 := NewBindContext(nil, nil) 1167 bc3.bindings = append(bc3.bindings, b3) 1168 1169 err = bc.mergeContexts(context.Background(), bc1, bc3) 1170 assert.NoError(t, err) 1171 1172 // a merge a, ctx is nil 1173 var ctx context.Context 1174 err = bc.mergeContexts(ctx, bc1, bc2) 1175 assert.Error(t, err) 1176 assert.EqualError(t, err, "invalid input: table 'a' specified more than once") 1177 }