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  }