github.com/matrixorigin/matrixone@v0.7.0/pkg/sql/plan/explain/marshal_query_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 explain 16 17 import ( 18 "bytes" 19 "context" 20 "encoding/json" 21 "testing" 22 23 "github.com/google/uuid" 24 plan2 "github.com/matrixorigin/matrixone/pkg/pb/plan" 25 "github.com/matrixorigin/matrixone/pkg/sql/parsers/dialect/mysql" 26 "github.com/matrixorigin/matrixone/pkg/sql/plan" 27 ) 28 29 func TestSimpleQueryToJson(t *testing.T) { 30 //input := "select * from part where p_container in ('SM CASE', 'SM BOX', 'SM PACK', 'SM PKG')" 31 //input := "SELECT DISTINCT N_NAME FROM NATION" 32 //input := "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" 33 sqls := []string{ 34 "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", 35 "SELECT N_NAME, N_REGIONKEY a FROM NATION WHERE N_REGIONKEY > 0 ORDER BY a DESC", //test alias 36 "SELECT N_NAME, count(distinct N_REGIONKEY) FROM NATION group by N_NAME", //test distinct agg function 37 "SELECT N_NAME, MAX(N_REGIONKEY) FROM NATION GROUP BY N_NAME HAVING MAX(N_REGIONKEY) > 10", //test agg 38 "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 39 "SELECT N_REGIONKEY FROM NATION where N_REGIONKEY >= N_NATIONKEY or (N_NAME like '%ddd' and N_REGIONKEY >0.5)", //test more expr 40 "SELECT N_NAME,N_REGIONKEY FROM NATION join REGION on NATION.N_REGIONKEY = REGION.R_REGIONKEY", 41 "SELECT N_NAME, N_REGIONKEY FROM NATION join REGION on NATION.N_REGIONKEY = REGION.R_REGIONKEY WHERE NATION.N_REGIONKEY > 0", 42 "SELECT N_NAME, NATION2.R_REGIONKEY FROM NATION2 join REGION using(R_REGIONKEY) WHERE NATION2.R_REGIONKEY > 0", 43 "select n_name from nation intersect all select n_name from nation2", 44 "select n_name from nation minus select n_name from nation2", 45 "select 1 union select 2 intersect select 2 union all select 1.1 minus select 22222", 46 "SELECT N_NAME, MAX(N_REGIONKEY) FROM NATION GROUP BY N_NAME HAVING MAX(N_REGIONKEY) > 10", 47 "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", 48 "SELECT N_NAME, N_REGIONKEY FROM NATION join REGION on NATION.N_REGIONKEY = REGION.R_REGIONKEY WHERE NATION.N_REGIONKEY > 0", 49 "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", 50 "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", 51 } 52 53 mock := plan.NewMockOptimizer(false) 54 buildPlanMarshalTest(mock, t, sqls) 55 } 56 57 func TestSingleTableQueryToJson(t *testing.T) { 58 sqls := []string{ 59 "SELECT '1900-01-01 00:00:00' + INTERVAL 2147483648 SECOND", 60 "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", 61 "SELECT N_NAME, N_REGIONKEY a FROM NATION WHERE N_REGIONKEY > 0 ORDER BY a DESC", //test alias 62 "SELECT NATION.N_NAME FROM NATION", //test alias 63 "SELECT * FROM NATION", //test star 64 "SELECT a.* FROM NATION a", //test star 65 "SELECT count(*) FROM NATION", //test star 66 "SELECT count(*) FROM NATION group by N_NAME", //test star 67 "SELECT N_NAME, count(distinct N_REGIONKEY) FROM NATION group by N_NAME", //test distinct agg function 68 "SELECT N_NAME, MAX(N_REGIONKEY) FROM NATION GROUP BY N_NAME HAVING MAX(N_REGIONKEY) > 10", //test agg 69 "SELECT DISTINCT N_NAME FROM NATION", //test distinct 70 "select sum(n_nationkey) as s from nation order by s", 71 "select date_add(date '2001-01-01', interval 1 day) as a", 72 "select date_sub(date '2001-01-01', interval '1' day) as a", 73 "select date_add('2001-01-01', interval '1' day) as a", 74 "select n_name, count(*) from nation group by n_name order by 2 asc", 75 "select count(distinct 12)", 76 "select nullif(n_name, n_comment), ifnull(n_comment, n_name) from nation", 77 78 "select 18446744073709551500", 79 "select 0xffffffffffffffff", 80 "select 0xffff", 81 82 "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 83 "SELECT N_REGIONKEY FROM NATION where N_REGIONKEY >= N_NATIONKEY or (N_NAME like '%ddd' and N_REGIONKEY >0.5)", //test more expr 84 "SELECT N_REGIONKEY FROM NATION where N_REGIONKEY between 2 and 2 OR N_NATIONKEY not between 3 and 10", //test more expr 85 // "SELECT N_REGIONKEY FROM NATION where N_REGIONKEY is null and N_NAME is not null", 86 "SELECT N_REGIONKEY FROM NATION where N_REGIONKEY IN (1, 2)", //test more expr 87 "SELECT N_REGIONKEY FROM NATION where N_REGIONKEY NOT IN (1)", //test more expr 88 "select N_REGIONKEY from nation group by N_REGIONKEY having abs(nation.N_REGIONKEY - 1) >10", 89 90 //"SELECT -1", 91 //"select date_add('1997-12-31 23:59:59',INTERVAL 100000 SECOND)", 92 //"select date_sub('1997-12-31 23:59:59',INTERVAL 2 HOUR)", 93 //"select @str_var, @int_var, @bool_var, @float_var, @null_var", 94 //"select @str_var, @@global.int_var, @@session.bool_var", 95 //"select n_name from nation where n_name != @str_var and n_regionkey > @int_var", 96 //"select n_name from nation where n_name != @@global.str_var and n_regionkey > @@session.int_var", 97 //"select distinct(n_name), ((abs(n_regionkey))) from nation", 98 //"SET @var = abs(-1), @@session.string_var = 'aaa'", 99 //"SET NAMES 'utf8mb4' COLLATE 'utf8mb4_general_ci'", 100 //"SELECT DISTINCT N_NAME FROM NATION ORDER BY N_NAME", //test distinct with order by 101 // 102 //"prepare stmt1 from select * from nation", 103 //"prepare stmt1 from select * from nation where n_name = ?", 104 //"prepare stmt1 from 'select * from nation where n_name = ?'", 105 //"prepare stmt1 from 'update nation set n_name = ? where n_nationkey > ?'", 106 //"prepare stmt1 from 'delete from nation where n_nationkey > ?'", 107 //"prepare stmt1 from 'insert into nation select * from nation2 where n_name = ?'", 108 //"prepare stmt1 from 'select * from nation where n_name = ?'", 109 //"prepare stmt1 from 'drop table if exists t1'", 110 //"prepare stmt1 from 'create table t1 (a int)'", 111 //"prepare stmt1 from select N_REGIONKEY from nation group by N_REGIONKEY having abs(nation.N_REGIONKEY - ?) > ?", 112 //"execute stmt1", 113 //"execute stmt1 using @str_var, @@global.int_var", 114 //"deallocate prepare stmt1", 115 //"drop prepare stmt1", 116 //"select count(n_name) from nation", 117 //"select l_shipdate + interval '1' day from lineitem", 118 //"select interval '1' day + l_shipdate from lineitem", 119 //"select interval '1' day + cast('2022-02-02 00:00:00' as datetime)", 120 //"select cast('2022-02-02 00:00:00' as datetime) + interval '1' day", 121 //"delete from nation", 122 //"delete nation, nation2 from nation join nation2 on nation.n_name = nation2.n_name", 123 } 124 mock := plan.NewMockOptimizer(false) 125 buildPlanMarshalTest(mock, t, sqls) 126 } 127 128 func TestJoinQueryToJson(t *testing.T) { 129 sqls := []string{ 130 "SELECT N_NAME,N_REGIONKEY FROM NATION join REGION on NATION.N_REGIONKEY = REGION.R_REGIONKEY", 131 "SELECT N_NAME, N_REGIONKEY FROM NATION join REGION on NATION.N_REGIONKEY = REGION.R_REGIONKEY WHERE NATION.N_REGIONKEY > 0", 132 "SELECT N_NAME, NATION2.R_REGIONKEY FROM NATION2 join REGION using(R_REGIONKEY) WHERE NATION2.R_REGIONKEY > 0", 133 "SELECT N_NAME, NATION2.R_REGIONKEY FROM NATION2 NATURAL JOIN REGION WHERE NATION2.R_REGIONKEY > 0", 134 "SELECT N_NAME FROM NATION NATURAL JOIN REGION", //have no same column name but it's ok 135 "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 136 "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 137 "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 138 "SELECT * FROM CUSTOMER c, ORDERS o, LINEITEM l WHERE c.C_CUSTKEY = o.O_CUSTKEY and l.L_ORDERKEY = o.O_ORDERKEY", //test star 139 "SELECT a.* FROM NATION a join REGION b on a.N_REGIONKEY = b.R_REGIONKEY WHERE a.N_REGIONKEY > 0", //test star 140 "SELECT * FROM NATION a join REGION b on a.N_REGIONKEY = b.R_REGIONKEY WHERE a.N_REGIONKEY > 0", 141 "SELECT N_NAME, R_REGIONKEY FROM NATION2 join REGION using(R_REGIONKEY)", 142 "select nation.n_name from nation join nation2 on nation.n_name !='a' join region on nation.n_regionkey = region.r_regionkey", 143 "select n_name from nation intersect select n_name from nation2", 144 "select n_name from nation minus select n_name from nation2", 145 } 146 mock := plan.NewMockOptimizer(false) 147 buildPlanMarshalTest(mock, t, sqls) 148 } 149 150 func TestNestedQueryToJson(t *testing.T) { 151 sqls := []string{ 152 "SELECT * FROM NATION where N_REGIONKEY > (select max(R_REGIONKEY) from REGION)", // unrelated 153 "SELECT * FROM NATION where N_REGIONKEY in (select max(R_REGIONKEY) from REGION)", // unrelated 154 "SELECT * FROM NATION where N_REGIONKEY not in (select max(R_REGIONKEY) from REGION)", // unrelated 155 "SELECT * FROM NATION where exists (select max(R_REGIONKEY) from REGION)", // unrelated 156 "SELECT * FROM NATION where N_REGIONKEY > (select max(R_REGIONKEY) from REGION where R_REGIONKEY = N_REGIONKEY)", // related 157 //"SELECT * FROM NATION where N_REGIONKEY > (select max(R_REGIONKEY) from REGION where R_REGIONKEY < N_REGIONKEY)", // related 158 //"DELETE FROM NATION WHERE N_NATIONKEY > 10", 159 `select 160 sum(l_extendedprice) / 7.0 as avg_yearly 161 from 162 lineitem, 163 part 164 where 165 p_partkey = l_partkey 166 and p_brand = 'Brand#54' 167 and p_container = 'LG BAG' 168 and l_quantity < ( 169 select 170 0.2 * avg(l_quantity) 171 from 172 lineitem 173 where 174 l_partkey = p_partkey 175 );`, //tpch q17 176 } 177 mock := plan.NewMockOptimizer(false) 178 buildPlanMarshalTest(mock, t, sqls) 179 } 180 181 func TestCollectionQueryToJson(t *testing.T) { 182 sqls := []string{ 183 "select 1 union select 2", 184 "select 1 union (select 2 union select 3)", 185 "(select 1 union select 2) union select 3 intersect select 4 order by 1", 186 "select 1 union select null", 187 "select n_name from nation intersect select n_name from nation2", 188 "select n_name from nation minus select n_name from nation2", 189 "select 1 union select 2 intersect select 2 union all select 1.1 minus select 22222", 190 "select 1 as a union select 2 order by a limit 1", 191 "select n_name from nation union select n_comment from nation order by n_name", 192 "with qn (foo, bar) as (select 1 as col, 2 as coll union select 4, 5) select qn1.bar from qn qn1", 193 "select n_name, n_comment from nation union all select n_name, n_comment from nation2", 194 "select n_name from nation intersect all select n_name from nation2", 195 "SELECT distinct(l.L_ORDERKEY) FROM LINEITEM AS l WHERE l.L_SHIPINSTRUCT='DELIVER IN PERSON' UNION SELECT distinct(l.L_ORDERKEY) FROM LINEITEM AS l WHERE l.L_SHIPMODE='AIR' OR l.L_SHIPMODE='AIR REG'", 196 "SELECT distinct(l.L_ORDERKEY) FROM LINEITEM AS l WHERE l.L_SHIPMODE IN ('AIR','AIR REG') EXCEPT SELECT distinct(l.L_ORDERKEY) FROM LINEITEM AS l WHERE l.L_SHIPINSTRUCT='DELIVER IN PERSON'", 197 } 198 199 mock := plan.NewMockOptimizer(false) 200 buildPlanMarshalTest(mock, t, sqls) 201 } 202 203 func TestDerivedTableQueryToJson(t *testing.T) { 204 // should pass 205 sqls := []string{ 206 "select c_custkey from (select c_custkey from CUSTOMER ) a", 207 "select c_custkey from (select c_custkey from CUSTOMER group by c_custkey ) a", 208 "select col1 from (select c_custkey from CUSTOMER group by c_custkey ) a(col1)", 209 "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", 210 "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", 211 "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", 212 "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", 213 "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", 214 } 215 mock := plan.NewMockOptimizer(false) 216 buildPlanMarshalTest(mock, t, sqls) 217 } 218 219 func TestDMLToJson(t *testing.T) { 220 sqls := []string{ 221 "INSERT INTO NATION SELECT * FROM NATION2", 222 "UPDATE NATION SET N_NAME ='U1', N_REGIONKEY=N_REGIONKEY+2 WHERE N_NATIONKEY > 10 LIMIT 20", 223 "UPDATE NATION,NATION2 SET NATION.N_NAME ='U1',NATION2.N_NATIONKEY=15 WHERE NATION.N_NATIONKEY = NATION2.N_NATIONKEY", 224 "DELETE FROM NATION WHERE N_NATIONKEY > 10", 225 "DELETE FROM a1, a2 USING NATION AS a1 INNER JOIN NATION2 AS a2 WHERE a1.N_NATIONKEY=a2.N_NATIONKEY", 226 } 227 mock := plan.NewMockOptimizer(true) 228 buildPlanMarshalTest(mock, t, sqls) 229 } 230 231 func buildPlanMarshalTest(opt plan.Optimizer, t *testing.T, sqls []string) { 232 ctx := context.TODO() 233 for _, sql := range sqls { 234 t.Logf("sql: %s \n", sql) 235 plan, err := runSingleSql(opt, t, sql) 236 if err != nil { 237 t.Fatalf("%+v", err) 238 } 239 queryPlan := plan.GetQuery() 240 for _, node := range queryPlan.Nodes { 241 info := plan2.AnalyzeInfo{ 242 InputRows: 12, 243 OutputRows: 12, 244 TimeConsumed: 5, 245 InputSize: 24, 246 OutputSize: 24, 247 MemorySize: 10, 248 } 249 node.AnalyzeInfo = &info 250 } 251 // generator query explain 252 explainQuery := NewExplainQueryImpl(queryPlan) 253 options := &ExplainOptions{ 254 Verbose: true, 255 Analyze: true, 256 Format: EXPLAIN_FORMAT_TEXT, 257 } 258 259 marshalPlan := explainQuery.BuildJsonPlan(ctx, uuid.New(), options) 260 //marshal, err := json.Marshal(marshalPlan) 261 262 buffer := &bytes.Buffer{} 263 encoder := json.NewEncoder(buffer) 264 encoder.SetEscapeHTML(false) 265 err = encoder.Encode(marshalPlan) 266 267 if err != nil { 268 panic(err) 269 } 270 t.Logf("SQL plan to json : %s\n", buffer.String()) 271 272 } 273 } 274 275 func runSingleSql(opt plan.Optimizer, t *testing.T, sql string) (*plan.Plan, error) { 276 stmts, err := mysql.Parse(opt.CurrentContext().GetContext(), sql) 277 if err != nil { 278 t.Fatalf("%+v", err) 279 } 280 // this sql always return one stmt 281 ctx := opt.CurrentContext() 282 return plan.BuildPlan(ctx, stmts[0]) 283 }