github.com/matrixorigin/matrixone@v1.2.0/pkg/sql/plan/partition_test.go (about)

     1  // Copyright 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  	"context"
    19  	"testing"
    20  
    21  	"github.com/matrixorigin/matrixone/pkg/container/types"
    22  	"github.com/matrixorigin/matrixone/pkg/pb/plan"
    23  	"github.com/matrixorigin/matrixone/pkg/sql/parsers"
    24  	"github.com/matrixorigin/matrixone/pkg/sql/parsers/dialect"
    25  	"github.com/matrixorigin/matrixone/pkg/sql/parsers/dialect/mysql"
    26  	"github.com/matrixorigin/matrixone/pkg/sql/parsers/tree"
    27  	"github.com/stretchr/testify/require"
    28  )
    29  
    30  func TestSingleDDLPartition(t *testing.T) {
    31  	//sql := `CREATE TABLE k1 (
    32  	//			id INT NOT NULL PRIMARY KEY,
    33  	//			name VARCHAR(20)
    34  	//		)
    35  	//		PARTITION BY KEY()
    36  	//		PARTITIONS 2;`
    37  
    38  	//sql := `CREATE TABLE k1 (
    39  	//			id INT NOT NULL,
    40  	//			name VARCHAR(20),
    41  	//			sal DOUBLE,
    42  	//			PRIMARY KEY (id, name)
    43  	//		)
    44  	//		PARTITION BY KEY()
    45  	//		PARTITIONS 2;`
    46  
    47  	sql := `CREATE TABLE k1 (
    48  				id INT NOT NULL,
    49  				name VARCHAR(20),
    50  				UNIQUE KEY (id)
    51  			)
    52  			PARTITION BY KEY()
    53  			PARTITIONS 2;`
    54  
    55  	//sql := `CREATE TABLE quarterly_report_status (
    56  	//		report_id INT NOT NULL,
    57  	//		report_status VARCHAR(20) NOT NULL,
    58  	//		report_updated TIMESTAMP NOT NULL
    59  	//	)
    60  	//		PARTITION BY RANGE ( UNIX_TIMESTAMP(report_updated) ) (
    61  	//		PARTITION p0 VALUES LESS THAN ( UNIX_TIMESTAMP('2008-01-01 00:00:00') ),
    62  	//		PARTITION p1 VALUES LESS THAN ( UNIX_TIMESTAMP('2008-04-01 00:00:00') ),
    63  	//		PARTITION p2 VALUES LESS THAN ( UNIX_TIMESTAMP('2008-07-01 00:00:00') ),
    64  	//		PARTITION p3 VALUES LESS THAN ( UNIX_TIMESTAMP('2008-10-01 00:00:00') ),
    65  	//		PARTITION p4 VALUES LESS THAN ( UNIX_TIMESTAMP('2009-01-01 00:00:00') ),
    66  	//		PARTITION p5 VALUES LESS THAN ( UNIX_TIMESTAMP('2009-04-01 00:00:00') ),
    67  	//		PARTITION p6 VALUES LESS THAN ( UNIX_TIMESTAMP('2009-07-01 00:00:00') ),
    68  	//		PARTITION p7 VALUES LESS THAN ( UNIX_TIMESTAMP('2009-10-01 00:00:00') ),
    69  	//		PARTITION p8 VALUES LESS THAN ( UNIX_TIMESTAMP('2010-01-01 00:00:00') ),
    70  	//		PARTITION p9 VALUES LESS THAN (MAXVALUE)
    71  	//	);`
    72  
    73  	mock := NewMockOptimizer(false)
    74  	logicPlan, err := buildSingleStmt(mock, t, sql)
    75  	if err != nil {
    76  		t.Fatalf("%+v", err)
    77  	}
    78  	outPutPlan(logicPlan, true, t)
    79  }
    80  
    81  func TestPartitionError(t *testing.T) {
    82  	sqls := []string{
    83  		`create temporary table p_table_non(col1 int,col2 varchar(25))partition by key(col2)partitions 4;`,
    84  	}
    85  
    86  	mock := NewMockOptimizer(false)
    87  	for _, sql := range sqls {
    88  		_, err := buildSingleStmt(mock, t, sql)
    89  		t.Log(sql)
    90  		t.Log(err)
    91  		if err == nil {
    92  			t.Fatalf("%+v", err)
    93  		}
    94  	}
    95  }
    96  
    97  func TestPartitioningKeysUniqueKeys(t *testing.T) {
    98  	sqls := []string{
    99  		`CREATE TABLE t1 (
   100  			col1 INT  NOT NULL,
   101  			col2 DATE NOT NULL,
   102  			col3 INT NOT NULL,
   103  			col4 INT NOT NULL,
   104  			UNIQUE KEY (col1, col2)
   105  		)
   106  		PARTITION BY KEY()
   107  		PARTITIONS 4;`,
   108  
   109  		`CREATE TABLE t1 (
   110  				col1 INT NOT NULL,
   111  				col2 DATE NOT NULL,
   112  				col3 INT NOT NULL,
   113  				col4 INT NOT NULL,
   114  				UNIQUE KEY (col1, col2, col3)
   115  			)
   116  			PARTITION BY HASH(col3)
   117  			PARTITIONS 4;`,
   118  
   119  		`CREATE TABLE t2 (
   120  				col1 INT NOT NULL,
   121  				col2 DATE NOT NULL,
   122  				col3 INT NOT NULL,
   123  				col4 INT NOT NULL,
   124  				UNIQUE KEY (col1, col3)
   125  			)
   126  			PARTITION BY HASH(col1 + col3)
   127  			PARTITIONS 4;`,
   128  
   129  		`CREATE TABLE t1 (
   130  			col1 INT NOT NULL,
   131  			col2 DATE NOT NULL,
   132  			col3 INT NOT NULL,
   133  			col4 INT NOT NULL,
   134  			UNIQUE KEY (col1, col2, col3)
   135  		)
   136  			PARTITION BY KEY(col3)
   137  			PARTITIONS 4;`,
   138  
   139  		`CREATE TABLE t2 (
   140  			col1 INT NOT NULL,
   141  			col2 DATE NOT NULL,
   142  			col3 INT NOT NULL,
   143  			col4 INT NOT NULL,
   144  			UNIQUE KEY (col1, col3)
   145  		)
   146  			PARTITION BY KEY(col1,col3)
   147  			PARTITIONS 4;`,
   148  
   149  		`CREATE TABLE t3 (
   150  			col1 INT NOT NULL,
   151  			col2 DATE NOT NULL,
   152  			col3 INT NOT NULL,
   153  			col4 INT NOT NULL,
   154  			UNIQUE KEY (col1, col2, col3),
   155  			UNIQUE KEY (col3)
   156  		)
   157  			PARTITION BY HASH(col3)
   158  			PARTITIONS 4;`,
   159  
   160  		`CREATE TABLE t3 (
   161  			col1 INT NOT NULL,
   162  			col2 DATE NOT NULL,
   163  			col3 INT NOT NULL,
   164  			col4 INT NOT NULL,
   165  			UNIQUE KEY (col1, col2, col3),
   166  			UNIQUE KEY (col3)
   167  		)
   168  			PARTITION BY KEY(col3)
   169  			PARTITIONS 4;`,
   170  
   171  		`CREATE TABLE t4 (
   172  			col1 INT NOT NULL,
   173  			col2 DATE NOT NULL,
   174  			col3 INT NOT NULL UNIQUE,
   175  			col4 INT NOT NULL
   176  		)
   177  			PARTITION BY KEY(col3)
   178  			PARTITIONS 4;`,
   179  	}
   180  
   181  	mock := NewMockOptimizer(false)
   182  	for _, sql := range sqls {
   183  		t.Log(sql)
   184  		logicPlan, err := buildSingleStmt(mock, t, sql)
   185  		if err != nil {
   186  			t.Fatalf("%+v", err)
   187  		}
   188  		outPutPlan(logicPlan, true, t)
   189  	}
   190  }
   191  
   192  func TestPartitioningKeysUniqueKeysError(t *testing.T) {
   193  	sqls := []string{
   194  		`CREATE TABLE t1 (
   195  			col1 INT NOT NULL,
   196  			col2 DATE NOT NULL,
   197  			col3 INT NOT NULL,
   198  			col4 INT NOT NULL,
   199  			UNIQUE KEY (col1, col2)
   200  		)
   201  		PARTITION BY HASH(col3)
   202  		PARTITIONS 4;`,
   203  
   204  		`CREATE TABLE t2 (
   205  			col1 INT NOT NULL,
   206  			col2 DATE NOT NULL,
   207  			col3 INT NOT NULL,
   208  			col4 INT NOT NULL,
   209  			UNIQUE KEY (col1),
   210  			UNIQUE KEY (col3)
   211  		)
   212  		PARTITION BY HASH(col1 + col3)
   213  		PARTITIONS 4;`,
   214  
   215  		`CREATE TABLE t1 (
   216  			col1 INT UNIQUE NOT NULL,
   217  			col2 DATE NOT NULL,
   218  			col3 INT NOT NULL,
   219  			col4 INT NOT NULL
   220  		)
   221  		PARTITION BY HASH(col3)
   222  		PARTITIONS 4;`,
   223  
   224  		`CREATE TABLE t2 (
   225  			col1 INT NOT NULL,
   226  			col2 DATE NOT NULL,
   227  			col3 INT NOT NULL,
   228  			col4 INT NOT NULL,
   229  			UNIQUE KEY (col1),
   230  			UNIQUE KEY (col3)
   231  		)
   232  		PARTITION BY KEY(col1,col3)
   233  		PARTITIONS 4;`,
   234  
   235  		`CREATE TABLE t3 (
   236  			col1 INT NOT NULL,
   237  			col2 DATE NOT NULL,
   238  			col3 INT NOT NULL,
   239  			col4 INT NOT NULL,
   240  			UNIQUE KEY (col1, col2),
   241  			UNIQUE KEY (col3)
   242  		)
   243  			PARTITION BY HASH(col1 + col3)
   244  			PARTITIONS 4;`,
   245  
   246  		`CREATE TABLE t3 (
   247  			col1 INT NOT NULL,
   248  			col2 DATE NOT NULL,
   249  			col3 INT NOT NULL,
   250  			col4 INT NOT NULL,
   251  			UNIQUE KEY (col1, col2),
   252  			UNIQUE KEY (col3)
   253  		)
   254  			PARTITION BY KEY(col1, col3)
   255  			PARTITIONS 4;`,
   256  		// should show error:Field in list of fields for partition function not found in table
   257  		`CREATE TABLE k1 (
   258  			id INT NOT NULL,
   259  			name VARCHAR(20),
   260  			sal DOUBLE
   261  		)
   262  		PARTITION BY KEY()
   263  		PARTITIONS 2;`,
   264  
   265  		`CREATE TABLE t6 (
   266  		col1 INT NOT NULL,
   267  		col2 DATE NOT NULL,
   268  		col3 INT NOT NULL UNIQUE,
   269  		col4 INT NOT NULL
   270  	   )
   271  		PARTITION BY KEY(col1)
   272  		PARTITIONS 4;`,
   273  
   274  		`CREATE TABLE t7 (
   275  		col1 INT NOT NULL,
   276  		col2 DATE NOT NULL,
   277  		col3 INT NOT NULL UNIQUE,
   278  		col4 INT NOT NULL
   279  	   )
   280  		PARTITION BY HASH(col4)
   281  		PARTITIONS 4;`,
   282  	}
   283  
   284  	mock := NewMockOptimizer(false)
   285  	for _, sql := range sqls {
   286  		_, err := buildSingleStmt(mock, t, sql)
   287  		t.Log(sql)
   288  		t.Log(err)
   289  		if err == nil {
   290  			t.Fatalf("%+v", err)
   291  		}
   292  	}
   293  }
   294  
   295  func TestPartitioningKeysPrimaryKeys(t *testing.T) {
   296  	sqls := []string{
   297  		`CREATE TABLE t7 (
   298  			col1 INT NOT NULL,
   299  			col2 DATE NOT NULL,
   300  			col3 INT NOT NULL,
   301  			col4 INT NOT NULL,
   302  			PRIMARY KEY(col1, col2)
   303  		)
   304  		PARTITION BY HASH(col1 + YEAR(col2))
   305  		PARTITIONS 4;`,
   306  
   307  		`CREATE TABLE t8 (
   308  			col1 INT NOT NULL,
   309  			col2 DATE NOT NULL,
   310  			col3 INT NOT NULL,
   311  			col4 INT NOT NULL,
   312  			PRIMARY KEY(col1, col2, col4),
   313  			UNIQUE KEY(col2, col1)
   314  		)
   315  		PARTITION BY HASH(col1 + YEAR(col2))
   316  		PARTITIONS 4;`,
   317  
   318  		`CREATE TABLE t7 (
   319  			col1 INT NOT NULL,
   320  			col2 DATE NOT NULL,
   321  			col3 INT NOT NULL,
   322  			col4 INT NOT NULL,
   323  			PRIMARY KEY(col1, col2)
   324  		)
   325  		PARTITION BY KEY(col1,col2)
   326  		PARTITIONS 4;`,
   327  
   328  		`CREATE TABLE t8 (
   329  			col1 INT NOT NULL,
   330  			col2 DATE NOT NULL,
   331  			col3 INT NOT NULL,
   332  			col4 INT NOT NULL,
   333  			PRIMARY KEY(col1, col2, col4),
   334  			UNIQUE KEY(col2, col1)
   335  		)
   336  		PARTITION BY KEY(col1,col2)
   337  		PARTITIONS 4;`,
   338  
   339  		`CREATE TABLE k1 (
   340  			id INT NOT NULL,
   341  			name VARCHAR(20),
   342  			sal DOUBLE,
   343  			PRIMARY KEY (id, name),
   344  			unique key (id)
   345  		)
   346  		PARTITION BY KEY(id)
   347  		PARTITIONS 2;`,
   348  	}
   349  
   350  	mock := NewMockOptimizer(false)
   351  	for _, sql := range sqls {
   352  		t.Log(sql)
   353  		logicPlan, err := buildSingleStmt(mock, t, sql)
   354  		if err != nil {
   355  			t.Fatalf("%+v", err)
   356  		}
   357  		outPutPlan(logicPlan, true, t)
   358  	}
   359  }
   360  
   361  func TestPartitioningKeysPrimaryKeysError(t *testing.T) {
   362  	sqls := []string{
   363  		`CREATE TABLE t5 (
   364  			col1 INT NOT NULL,
   365  			col2 DATE NOT NULL,
   366  			col3 INT NOT NULL,
   367  			col4 INT NOT NULL,
   368  			PRIMARY KEY(col1, col2)
   369  		)
   370  		PARTITION BY HASH(col3)
   371  		PARTITIONS 4;`,
   372  
   373  		`CREATE TABLE t6 (
   374  			col1 INT NOT NULL,
   375  			col2 DATE NOT NULL,
   376  			col3 INT NOT NULL,
   377  			col4 INT NOT NULL,
   378  			PRIMARY KEY(col1, col3),
   379  			UNIQUE KEY(col2)
   380  		)
   381  		PARTITION BY HASH( YEAR(col2) )
   382  		PARTITIONS 4;`,
   383  
   384  		`CREATE TABLE t5 (
   385  			col1 INT NOT NULL,
   386  			col2 DATE NOT NULL,
   387  			col3 INT NOT NULL,
   388  			col4 INT NOT NULL,
   389  			PRIMARY KEY(col1, col2)
   390  		)
   391  		PARTITION BY KEY(col3)
   392  		PARTITIONS 4;`,
   393  
   394  		`CREATE TABLE t6 (
   395  			col1 INT NOT NULL,
   396  			col2 DATE NOT NULL,
   397  			col3 INT NOT NULL,
   398  			col4 INT NOT NULL,
   399  			PRIMARY KEY(col1, col3),
   400  			UNIQUE KEY(col2)
   401  		)
   402  		PARTITION BY KEY(col2)
   403  		PARTITIONS 4;`,
   404  	}
   405  
   406  	mock := NewMockOptimizer(false)
   407  	for _, sql := range sqls {
   408  		_, err := buildSingleStmt(mock, t, sql)
   409  		t.Log(sql)
   410  		t.Log(err)
   411  		if err == nil {
   412  			t.Fatalf("%+v", err)
   413  		}
   414  	}
   415  }
   416  
   417  // A UNIQUE INDEX must include all columns in the table's partitioning function
   418  func TestPartitionKeysShouldShowError(t *testing.T) {
   419  	sqls := []string{
   420  		`CREATE TABLE t4 (
   421  				col1 INT NOT NULL,
   422  				col2 INT NOT NULL,
   423  				col3 INT NOT NULL,
   424  				col4 INT NOT NULL,
   425  				UNIQUE KEY (col1, col3),
   426  				UNIQUE KEY (col2, col4)
   427  			)
   428  			PARTITION BY KEY(col1, col3)
   429  			PARTITIONS 2;`,
   430  
   431  		`CREATE TABLE t4 (
   432  				col1 INT NOT NULL,
   433  				col2 INT NOT NULL,
   434  				col3 INT NOT NULL,
   435  				col4 INT NOT NULL,
   436  				UNIQUE KEY (col1, col3),
   437  				UNIQUE KEY (col2, col4)
   438  			)
   439  			PARTITION BY HASH(col1 + col3)
   440  			PARTITIONS 2;`,
   441  
   442  		`CREATE TABLE t4 (
   443  				col1 INT NOT NULL,
   444  				col2 INT NOT NULL,
   445  				col3 INT NOT NULL,
   446  				col4 INT NOT NULL,
   447  				UNIQUE KEY (col1, col3),
   448  				UNIQUE KEY (col2, col4)
   449  			)
   450  			PARTITION BY RANGE (col1 + col3) (
   451  			PARTITION p0 VALUES LESS THAN (6),
   452  			PARTITION p1 VALUES LESS THAN (11),
   453  			PARTITION p2 VALUES LESS THAN (16),
   454  			PARTITION p3 VALUES LESS THAN (21)
   455  			);`,
   456  
   457  		`CREATE TABLE t4 (
   458  				col1 INT NOT NULL,
   459  				col2 INT NOT NULL,
   460  				col3 INT NOT NULL,
   461  				col4 INT NOT NULL,
   462  				UNIQUE KEY (col1, col3),
   463  				UNIQUE KEY (col2, col4)
   464  			)
   465  			PARTITION BY RANGE COLUMNS(col1, col3) PARTITIONS 4 (
   466  			PARTITION p0 VALUES LESS THAN (10,5),
   467  			PARTITION p1 VALUES LESS THAN (20,10),
   468  			PARTITION p2 VALUES LESS THAN (50,20),
   469  			PARTITION p3 VALUES LESS THAN (65,30)
   470  			);`,
   471  
   472  		`CREATE TABLE t4 (
   473  				col1 INT NOT NULL,
   474  				col2 INT NOT NULL,
   475  				col3 INT NOT NULL,
   476  				col4 INT NOT NULL,
   477  				UNIQUE KEY (col1),
   478  				UNIQUE KEY (col2, col4)
   479  			)
   480  			PARTITION BY LIST (col1) (
   481  			PARTITION r0 VALUES IN (1, 5, 9, 13, 17, 21),
   482  			PARTITION r1 VALUES IN (2, 6, 10, 14, 18, 22),
   483  			PARTITION r2 VALUES IN (3, 7, 11, 15, 19, 23),
   484  			PARTITION r3 VALUES IN (4, 8, 12, 16, 20, 24)
   485  			);`,
   486  
   487  		`CREATE TABLE t4 (
   488  				col1 INT NOT NULL,
   489  				col2 INT NOT NULL,
   490  				col3 INT NOT NULL,
   491  				col4 INT NOT NULL,
   492  				UNIQUE KEY (col1, col3),
   493  				UNIQUE KEY (col2, col4)
   494  			)
   495  			PARTITION BY LIST COLUMNS(col1, col3) (
   496  			PARTITION p0 VALUES IN( (0,0), (NULL,NULL) ),
   497  			PARTITION p1 VALUES IN( (0,1), (0,2), (0,3), (1,1), (1,2) ),
   498  			PARTITION p2 VALUES IN( (1,0), (2,0), (2,1), (3,0), (3,1) ),
   499  			PARTITION p3 VALUES IN( (1,3), (2,2), (2,3), (3,2), (3,3) )
   500  			);`,
   501  	}
   502  	mock := NewMockOptimizer(false)
   503  	for _, sql := range sqls {
   504  		_, err := buildSingleStmt(mock, t, sql)
   505  		t.Log(sql)
   506  		t.Log(err)
   507  		if err == nil {
   508  			t.Fatalf("%+v", err)
   509  		}
   510  	}
   511  }
   512  
   513  func buildSingleStmt(opt Optimizer, t *testing.T, sql string) (*Plan, error) {
   514  	statements, err := mysql.Parse(opt.CurrentContext().GetContext(), sql, 1, 0)
   515  	if err != nil {
   516  		return nil, err
   517  	}
   518  	// this sql always return single statement
   519  	context := opt.CurrentContext()
   520  	plan, err := BuildPlan(context, statements[0], false)
   521  	if plan != nil {
   522  		testDeepCopy(plan)
   523  	}
   524  	return plan, err
   525  }
   526  
   527  func Test_checkUniqueKeyIncludePartKey(t *testing.T) {
   528  	partKeys := map[string]int{
   529  		"a": 0,
   530  		"b": 0,
   531  		"c": 0,
   532  	}
   533  
   534  	partKeys2 := map[string]int{
   535  		"a": 0,
   536  		"b": 0,
   537  		"e": 0,
   538  	}
   539  
   540  	uniqueKeys := map[string]int{
   541  		"a": 0,
   542  		"b": 0,
   543  		"c": 0,
   544  		"d": 0,
   545  	}
   546  
   547  	r1 := checkUniqueKeyIncludePartKey(partKeys, uniqueKeys)
   548  	require.True(t, r1)
   549  	r2 := checkUniqueKeyIncludePartKey(partKeys2, uniqueKeys)
   550  	require.False(t, r2)
   551  
   552  	r3 := findColumnInIndexCols("a", uniqueKeys)
   553  	require.True(t, r3)
   554  
   555  	r4 := findColumnInIndexCols("e", uniqueKeys)
   556  	require.False(t, r4)
   557  
   558  	x := make(map[string]int)
   559  	r5 := findColumnInIndexCols("e", x)
   560  	require.False(t, r5)
   561  
   562  	x["e"] = 0
   563  	r6 := findColumnInIndexCols("e", x)
   564  	require.True(t, r6)
   565  }
   566  
   567  func mockPartitionBinder(tableDef *plan.TableDef) (*PartitionBinder, error) {
   568  	mock := NewMockOptimizer(false)
   569  	builder := NewQueryBuilder(plan.Query_SELECT, mock.CurrentContext(), false)
   570  	bindContext := NewBindContext(builder, nil)
   571  	nodeID := builder.appendNode(&plan.Node{
   572  		NodeType:    plan.Node_TABLE_SCAN,
   573  		Stats:       nil,
   574  		ObjRef:      nil,
   575  		TableDef:    tableDef,
   576  		BindingTags: []int32{builder.genNewTag()},
   577  	}, bindContext)
   578  
   579  	err := builder.addBinding(nodeID, tree.AliasClause{}, bindContext)
   580  	if err != nil {
   581  		return nil, err
   582  	}
   583  	return NewPartitionBinder(builder, bindContext), err
   584  }
   585  
   586  func mockExpr(t *testing.T, s string) (tree.Expr, error) {
   587  	selStr := "select " + s
   588  	one, err := parsers.ParseOne(context.TODO(), dialect.MYSQL, selStr, 1, 0)
   589  	require.Nil(t, err)
   590  	return one.(*tree.Select).Select.(*tree.SelectClause).Exprs[0].Expr, err
   591  }
   592  
   593  func Test_checkPartitionKeys(t *testing.T) {
   594  	addCol := func(tableDef *TableDef, col *ColDef) {
   595  		tableDef.Cols = append(tableDef.Cols, col)
   596  	}
   597  
   598  	/*
   599  		table test:
   600  		col1 int32 pk
   601  		col2 int32
   602  	*/
   603  	tableDef := &plan.TableDef{
   604  		Name: "test",
   605  		Pkey: &plan.PrimaryKeyDef{
   606  			Names: []string{"col1"},
   607  		},
   608  	}
   609  
   610  	addCol(tableDef, &ColDef{
   611  		Name: "col1",
   612  		Typ: plan.Type{
   613  			Id: int32(types.T_int8),
   614  		},
   615  	})
   616  	addCol(tableDef, &ColDef{
   617  		Name: "col2",
   618  		Typ: plan.Type{
   619  			Id: int32(types.T_int8),
   620  		},
   621  	})
   622  
   623  	/*
   624  		table test2:
   625  		col1 int32 pk
   626  		col2 int32 pk
   627  		col3 int32
   628  
   629  	*/
   630  	tableDef2 := &plan.TableDef{
   631  		Name: "test2",
   632  		Pkey: &plan.PrimaryKeyDef{
   633  			Names: []string{"col1", "col2"},
   634  		},
   635  	}
   636  
   637  	addCol(tableDef2, &ColDef{
   638  		Name: "col1",
   639  		Typ: plan.Type{
   640  			Id: int32(types.T_int8),
   641  		},
   642  	})
   643  	addCol(tableDef2, &ColDef{
   644  		Name: "col2",
   645  		Typ: plan.Type{
   646  			Id: int32(types.T_int8),
   647  		},
   648  	})
   649  	addCol(tableDef2, &ColDef{
   650  		Name: "col3",
   651  		Typ: plan.Type{
   652  			Id: int32(types.T_int8),
   653  		},
   654  	})
   655  
   656  	addIndex := func(tableDef *TableDef, index string, names ...string) {
   657  		tableDef.Indexes = append(tableDef.Indexes, &IndexDef{
   658  			Unique: true,
   659  			Parts:  names,
   660  		})
   661  	}
   662  
   663  	addIndex(tableDef2, "index1", "col1", "col2")
   664  
   665  	{
   666  		//partition keys [col1,col2] error
   667  		pb, err := mockPartitionBinder(tableDef)
   668  		require.Nil(t, err)
   669  
   670  		astExpr, err := mockExpr(t, "col1+1+col2")
   671  		require.Nil(t, err)
   672  
   673  		expr, err := pb.BindExpr(astExpr, 0, true)
   674  		require.Nil(t, err)
   675  
   676  		partDef := &PartitionByDef{}
   677  		partDef.PartitionExpr = &plan.PartitionExpr{
   678  			Expr: expr,
   679  		}
   680  
   681  		err = checkPartitionKeys(context.TODO(), pb.builder.nameByColRef, tableDef, partDef)
   682  		require.NotNil(t, err)
   683  	}
   684  	{
   685  		//partition keys [col1]
   686  		pb, err := mockPartitionBinder(tableDef)
   687  		require.Nil(t, err)
   688  
   689  		astExpr, err := mockExpr(t, "col1+1")
   690  		require.Nil(t, err)
   691  
   692  		expr, err := pb.BindExpr(astExpr, 0, true)
   693  		require.Nil(t, err)
   694  
   695  		partDef := &PartitionByDef{}
   696  		partDef.PartitionExpr = &plan.PartitionExpr{
   697  			Expr: expr,
   698  		}
   699  
   700  		err = checkPartitionKeys(context.TODO(), pb.builder.nameByColRef, tableDef, partDef)
   701  		require.Nil(t, err)
   702  	}
   703  	{
   704  		//partition keys []
   705  		pb, err := mockPartitionBinder(tableDef)
   706  		require.Nil(t, err)
   707  
   708  		astExpr, err := mockExpr(t, "1")
   709  		require.Nil(t, err)
   710  
   711  		expr, err := pb.BindExpr(astExpr, 0, true)
   712  		require.Nil(t, err)
   713  
   714  		partDef := &PartitionByDef{}
   715  		partDef.PartitionExpr = &plan.PartitionExpr{
   716  			Expr: expr,
   717  		}
   718  
   719  		err = checkPartitionKeys(context.TODO(), pb.builder.nameByColRef, tableDef, partDef)
   720  		require.Nil(t, err)
   721  	}
   722  	{
   723  		//partition keys [col1,col2]
   724  		pb, err := mockPartitionBinder(tableDef2)
   725  		require.Nil(t, err)
   726  
   727  		astExpr, err := mockExpr(t, "col1+1+col2")
   728  		require.Nil(t, err)
   729  
   730  		expr, err := pb.BindExpr(astExpr, 0, true)
   731  		require.Nil(t, err)
   732  
   733  		partDef := &PartitionByDef{}
   734  		partDef.PartitionExpr = &plan.PartitionExpr{
   735  			Expr: expr,
   736  		}
   737  
   738  		err = checkPartitionKeys(context.TODO(), pb.builder.nameByColRef, tableDef2, partDef)
   739  		require.Nil(t, err)
   740  	}
   741  	addIndex(tableDef2, "index2", "col1")
   742  	{
   743  		//partition keys [col1,col2]
   744  		pb, err := mockPartitionBinder(tableDef2)
   745  		require.Nil(t, err)
   746  
   747  		astExpr, err := mockExpr(t, "col1+1+col2")
   748  		require.Nil(t, err)
   749  
   750  		expr, err := pb.BindExpr(astExpr, 0, true)
   751  		require.Nil(t, err)
   752  
   753  		partDef := &PartitionByDef{}
   754  		partDef.PartitionExpr = &plan.PartitionExpr{
   755  			Expr: expr,
   756  		}
   757  
   758  		err = checkPartitionKeys(context.TODO(), pb.builder.nameByColRef, tableDef2, partDef)
   759  		require.NotNil(t, err)
   760  	}
   761  	{
   762  		//partition keys [col2]
   763  		pb, err := mockPartitionBinder(tableDef2)
   764  		require.Nil(t, err)
   765  
   766  		astExpr, err := mockExpr(t, "col2")
   767  		require.Nil(t, err)
   768  
   769  		expr, err := pb.BindExpr(astExpr, 0, true)
   770  		require.Nil(t, err)
   771  
   772  		partDef := &PartitionByDef{}
   773  		partDef.PartitionExpr = &plan.PartitionExpr{
   774  			Expr: expr,
   775  		}
   776  
   777  		err = checkPartitionKeys(context.TODO(), pb.builder.nameByColRef, tableDef2, partDef)
   778  		require.NotNil(t, err)
   779  	}
   780  	{
   781  		//partition columns [col1]
   782  		partDef := &PartitionByDef{}
   783  		partDef.PartitionColumns = &plan.PartitionColumns{
   784  			PartitionColumns: []string{"col1"},
   785  		}
   786  
   787  		err := checkPartitionKeys(context.TODO(), nil, tableDef2, partDef)
   788  		require.Nil(t, err)
   789  	}
   790  	{
   791  		//partition columns [col1,col1]
   792  		partDef := &PartitionByDef{}
   793  		partDef.PartitionColumns = &plan.PartitionColumns{
   794  			PartitionColumns: []string{"col1", "col1"},
   795  		}
   796  
   797  		err := checkPartitionKeys(context.TODO(), nil, tableDef2, partDef)
   798  		require.NotNil(t, err)
   799  	}
   800  	{
   801  		//partition columns [col1]
   802  		partDef := &PartitionByDef{}
   803  		partDef.PartitionColumns = &plan.PartitionColumns{
   804  			PartitionColumns: []string{"col1"},
   805  		}
   806  		tableDef2.Pkey = &PrimaryKeyDef{
   807  			Names: []string{"col1", "col1"},
   808  		}
   809  		err := checkPartitionKeys(context.TODO(), nil, tableDef2, partDef)
   810  		require.NotNil(t, err)
   811  	}
   812  	{
   813  		//partition columns [col1]
   814  		partDef := &PartitionByDef{}
   815  		partDef.PartitionColumns = &plan.PartitionColumns{
   816  			PartitionColumns: []string{"col1"},
   817  		}
   818  		tableDef2.Indexes[0].Parts = []string{
   819  			"col1", "col1",
   820  		}
   821  		err := checkPartitionKeys(context.TODO(), nil, tableDef2, partDef)
   822  		require.NotNil(t, err)
   823  	}
   824  }
   825  
   826  func addCol(tableDef *TableDef, col *ColDef) {
   827  	tableDef.Cols = append(tableDef.Cols, col)
   828  }
   829  
   830  func Test_checkPartitionExprType(t *testing.T) {
   831  	/*
   832  		table test:
   833  		col1 int32 pk
   834  		col2 int32
   835  	*/
   836  	tableDef := &plan.TableDef{
   837  		Name: "test",
   838  		Pkey: &plan.PrimaryKeyDef{
   839  			Names: []string{"col1"},
   840  		},
   841  	}
   842  
   843  	addCol(tableDef, &ColDef{
   844  		Name: "col1",
   845  		Typ: plan.Type{
   846  			Id: int32(types.T_int8),
   847  		},
   848  	})
   849  	addCol(tableDef, &ColDef{
   850  		Name: "col2",
   851  		Typ: plan.Type{
   852  			Id: int32(types.T_int8),
   853  		},
   854  	})
   855  
   856  	{
   857  		//partition keys [col1]
   858  		pb, err := mockPartitionBinder(tableDef)
   859  		require.Nil(t, err)
   860  
   861  		astExpr, err := mockExpr(t, "col1")
   862  		require.Nil(t, err)
   863  
   864  		expr, err := pb.BindExpr(astExpr, 0, true)
   865  		require.Nil(t, err)
   866  
   867  		partDef := &PartitionByDef{}
   868  		partDef.PartitionExpr = &plan.PartitionExpr{
   869  			Expr: expr,
   870  		}
   871  
   872  		err = checkPartitionExprType(context.TODO(), nil, nil, partDef)
   873  		require.Nil(t, err)
   874  	}
   875  	{
   876  		//partition keys [col1]
   877  		pb, err := mockPartitionBinder(tableDef)
   878  		require.Nil(t, err)
   879  
   880  		astExpr, err := mockExpr(t, "col1 / 3")
   881  		require.Nil(t, err)
   882  
   883  		expr, err := pb.BindExpr(astExpr, 0, true)
   884  		require.NotNil(t, err)
   885  		require.Nil(t, expr)
   886  
   887  	}
   888  }
   889  
   890  func Test_stringSliceToMap(t *testing.T) {
   891  	smap := make(map[string]int)
   892  	r1, _ := stringSliceToMap(nil, smap)
   893  	require.False(t, r1)
   894  
   895  	smap2 := make(map[string]int)
   896  	r2, _ := stringSliceToMap([]string{"a1", "a2"}, smap2)
   897  	require.False(t, r2)
   898  
   899  	smap3 := make(map[string]int)
   900  	r3, r31 := stringSliceToMap([]string{"a1", "a1"}, smap3)
   901  	require.True(t, r3)
   902  	require.Equal(t, r31, "a1")
   903  
   904  	smap4 := make(map[string]int)
   905  	r4, r41 := stringSliceToMap([]string{"", ""}, smap4)
   906  	require.True(t, r4)
   907  	require.Equal(t, r41, "")
   908  }
   909  
   910  func Test_checkDuplicatePartitionName(t *testing.T) {
   911  	{
   912  		partDef1 := &PartitionByDef{
   913  			Partitions: []*plan.PartitionItem{
   914  				{PartitionName: "p0"},
   915  				{PartitionName: "p2"},
   916  			},
   917  		}
   918  
   919  		err := checkPartitionNameUnique(context.TODO(), partDef1)
   920  		require.Nil(t, err)
   921  	}
   922  	{
   923  		partDef1 := &PartitionByDef{
   924  			Partitions: []*plan.PartitionItem{
   925  				{PartitionName: "p0"},
   926  				{PartitionName: "p0"},
   927  			},
   928  		}
   929  
   930  		err := checkPartitionNameUnique(context.TODO(), partDef1)
   931  		require.NotNil(t, err)
   932  	}
   933  }
   934  
   935  func Test_checkPartitionCount(t *testing.T) {
   936  	{
   937  		err := checkPartitionCount(context.TODO(), PartitionCountLimit-10)
   938  		require.Nil(t, err)
   939  	}
   940  	{
   941  		err := checkPartitionCount(context.TODO(), PartitionCountLimit+10)
   942  		require.NotNil(t, err)
   943  	}
   944  }
   945  
   946  func Test_checkDuplicatePartitionColumns(t *testing.T) {
   947  	{
   948  		partDef := &PartitionByDef{
   949  			PartitionColumns: &plan.PartitionColumns{
   950  				PartitionColumns: []string{},
   951  			},
   952  		}
   953  		err := checkPartitionColumnsUnique(context.TODO(), partDef)
   954  		require.Nil(t, err)
   955  	}
   956  	{
   957  		partDef := &PartitionByDef{}
   958  		err := checkPartitionColumnsUnique(context.TODO(), partDef)
   959  		require.Nil(t, err)
   960  	}
   961  	{
   962  		partDef := &PartitionByDef{
   963  			PartitionColumns: &plan.PartitionColumns{
   964  				PartitionColumns: []string{"a"},
   965  			},
   966  		}
   967  		err := checkPartitionColumnsUnique(context.TODO(), partDef)
   968  		require.Nil(t, err)
   969  	}
   970  	{
   971  		partDef := &PartitionByDef{
   972  			PartitionColumns: &plan.PartitionColumns{
   973  				PartitionColumns: []string{"a", "a"},
   974  			},
   975  		}
   976  		err := checkPartitionColumnsUnique(context.TODO(), partDef)
   977  		require.NotNil(t, err)
   978  	}
   979  	{
   980  		partDef := &PartitionByDef{
   981  			PartitionColumns: &plan.PartitionColumns{
   982  				PartitionColumns: []string{"a", "b"},
   983  			},
   984  		}
   985  		err := checkPartitionColumnsUnique(context.TODO(), partDef)
   986  		require.Nil(t, err)
   987  	}
   988  }
   989  
   990  func TestCheckPartitionFuncValid(t *testing.T) {
   991  	/*
   992  		create table test(
   993  			col1 int8,
   994  			col2 int8,
   995  			col3 date,
   996  			PRIMARY KEY(col1, col2, col3)
   997  		);
   998  	*/
   999  	tableDef := &plan.TableDef{
  1000  		Name: "test",
  1001  		Pkey: &plan.PrimaryKeyDef{
  1002  			Names: []string{"col1", "col2", "col3"},
  1003  		},
  1004  	}
  1005  
  1006  	addCol(tableDef, &ColDef{
  1007  		Name: "col1",
  1008  		Typ: plan.Type{
  1009  			Id: int32(types.T_int8),
  1010  		},
  1011  	})
  1012  	addCol(tableDef, &ColDef{
  1013  		Name: "col2",
  1014  		Typ: plan.Type{
  1015  			Id: int32(types.T_int8),
  1016  		},
  1017  	})
  1018  	addCol(tableDef, &ColDef{
  1019  		Name: "col3",
  1020  		Typ: plan.Type{
  1021  			Id: int32(types.T_date),
  1022  		},
  1023  	})
  1024  
  1025  	type kase struct {
  1026  		s                        string
  1027  		wantPartitionFuncErr     bool
  1028  		wantPartitionExprTypeErr bool
  1029  	}
  1030  
  1031  	checkFunc := func(k kase) {
  1032  		//fmt.Println(k.s)
  1033  		pb, err := mockPartitionBinder(tableDef)
  1034  		require.Nil(t, err)
  1035  
  1036  		astExpr, err := mockExpr(t, k.s)
  1037  		require.Nil(t, err)
  1038  
  1039  		partitionDef := &PartitionByDef{
  1040  			Type: plan.PartitionType_HASH,
  1041  		}
  1042  
  1043  		err = buildPartitionExpr(context.TODO(), tableDef, pb, partitionDef, astExpr)
  1044  		if !k.wantPartitionFuncErr {
  1045  			require.Nil(t, err)
  1046  			require.NotNil(t, partitionDef.PartitionExpr.Expr)
  1047  
  1048  			err = checkPartitionExprType(context.TODO(), nil, nil, partitionDef)
  1049  			if !k.wantPartitionExprTypeErr {
  1050  				require.Nil(t, err)
  1051  			} else {
  1052  				require.NotNil(t, err)
  1053  			}
  1054  		} else {
  1055  			require.NotNil(t, err)
  1056  		}
  1057  	}
  1058  
  1059  	//---------------------------------------------------------------------------------
  1060  	rightCases := []kase{
  1061  		{"col1", false, false},
  1062  		{"col1 + col2", false, false},
  1063  		{"col1 - col2", false, false},
  1064  		{"col1 * col2", false, false},
  1065  		{"col1 div col2", false, false},
  1066  	}
  1067  
  1068  	for _, k := range rightCases {
  1069  		checkFunc(k)
  1070  	}
  1071  
  1072  	//--------------------------------------------------------------------------------
  1073  	wrongCases := []kase{
  1074  		{"col1 / 3", true, false},
  1075  		{"col1 / col2", true, false},
  1076  		{"col1 | col2", true, false},
  1077  		{"col1 & col2", true, false},
  1078  		{"col1 ^ col2", true, false},
  1079  		{"col1 << col2", true, false},
  1080  		{"col1 >> col2", true, false},
  1081  		{"~col2", true, false},
  1082  	}
  1083  	for _, k := range wrongCases {
  1084  		checkFunc(k)
  1085  	}
  1086  
  1087  	//--------------------------------------------------------------------------------
  1088  	supportedFuncCases := []kase{
  1089  		{"abs(col1)", false, false},
  1090  		{"abs(-1)", true, false},
  1091  		{"ceiling(col1)", false, false},
  1092  		{"ceiling(0.1)", true, false},
  1093  		{"datediff('2007-12-31 23:59:59','2007-12-30')", true, false}, // XXX: should fold datediff and then report error
  1094  		{"datediff(col3,'2007-12-30')", false, false},                 // re check it
  1095  		{"day(col3)", false, false},
  1096  		{"dayofyear(col3)", false, false},
  1097  		{"extract(year from col3)", false, false},
  1098  		{"extract(year_month from col3)", false, false},
  1099  		{"floor(col1)", false, false},
  1100  		{"floor(0.1)", true, false},
  1101  		{"hour(col3)", true, false},
  1102  		{"minute(col3)", true, false},
  1103  		{"mod(col1,3)", false, false},
  1104  		{"month(col3)", false, false},
  1105  		{"second(col3)", true, false},
  1106  		{"unix_timestamp(col3)", true, false},
  1107  		{"weekday(col3)", false, false},
  1108  		{"year(col3)", false, false},
  1109  		{"to_days(col3)", false, false},
  1110  		{"to_seconds(col3)", false, false},
  1111  	}
  1112  	for _, k := range supportedFuncCases {
  1113  		checkFunc(k)
  1114  	}
  1115  
  1116  	//-------------------------------------------------------------------------------------
  1117  	unsupportedFuncCases := []kase{
  1118  		{"dayofmonth(col3)", true, false},  //unimplement builtin function
  1119  		{"dayofweek(col3)", true, false},   //unimplement builtin function
  1120  		{"microsecond(col3)", true, false}, //unimplement builtin function
  1121  		{"quarter(col3)", true, false},     //unimplement builtin function
  1122  		{"time_to_sec(col3)", true, false}, //unimplement builtin function
  1123  		{"yearweek(col3)", true, false},    //unimplement builtin function
  1124  	}
  1125  	for _, k := range unsupportedFuncCases {
  1126  		checkFunc(k)
  1127  	}
  1128  }