github.com/matrixorigin/matrixone@v1.2.0/pkg/sql/plan/partition_hash_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  	"fmt"
    20  	"testing"
    21  
    22  	"github.com/matrixorigin/matrixone/pkg/container/types"
    23  	"github.com/matrixorigin/matrixone/pkg/pb/plan"
    24  	"github.com/matrixorigin/matrixone/pkg/sql/parsers"
    25  	"github.com/matrixorigin/matrixone/pkg/sql/parsers/dialect"
    26  	"github.com/matrixorigin/matrixone/pkg/sql/parsers/tree"
    27  	"github.com/stretchr/testify/require"
    28  )
    29  
    30  func TestCreateHashPartitionTable(t *testing.T) {
    31  	sql := "CREATE TABLE t1 (col1 INT, col2 CHAR(5), col3 DATE) PARTITION BY LINEAR HASH( YEAR(col3)) PARTITIONS 6;"
    32  	//sql := "create table p_hash_table_08(col1 tinyint,col2 varchar(30),col3 decimal(6,3))partition by hash(ceil(col3)) partitions 2;"
    33  	mock := NewMockOptimizer(false)
    34  	logicPlan, err := buildSingleStmt(mock, t, sql)
    35  	if err != nil {
    36  		t.Fatalf("%+v", err)
    37  	}
    38  	outPutPlan(logicPlan, true, t)
    39  }
    40  
    41  // -----------------------Hash Partition-------------------------------------
    42  func TestHashPartition(t *testing.T) {
    43  	// HASH(expr) Partition
    44  	sqls := []string{
    45  		"CREATE TABLE t1 (col1 INT, col2 CHAR(5)) PARTITION BY HASH(col1);",
    46  		"CREATE TABLE t1 (col1 INT, col2 CHAR(5)) PARTITION BY HASH(col1) PARTITIONS 4;",
    47  		//"CREATE TABLE t1 (col1 INT, col2 DECIMAL) PARTITION BY HASH(col2) PARTITIONS 4;",
    48  		"CREATE TABLE t1 (col1 INT, col2 CHAR(5), col3 DATETIME) PARTITION BY HASH (YEAR(col3));",
    49  		"CREATE TABLE t1 (col1 INT, col2 CHAR(5), col3 DATETIME) PARTITION BY HASH (YEAR(col3) + col1 % (7*24));",
    50  		"CREATE TABLE t1 (col1 INT, col2 CHAR(5), col3 DATE) PARTITION BY LINEAR HASH( YEAR(col3)) PARTITIONS 6;",
    51  		"create table t2 (a date, b datetime) partition by hash (EXTRACT(YEAR_MONTH FROM a)) partitions 7",
    52  		"create table t3 (a int, b int) partition by hash(ceiling(a-b)) partitions 10",
    53  		"create table t4 (a int, b int) partition by hash(floor(a-b)) partitions 10",
    54  		`CREATE TABLE employees (
    55  				id INT NOT NULL,
    56  				fname VARCHAR(30),
    57  				lname VARCHAR(30),
    58  				hired DATE NOT NULL DEFAULT '1970-01-01',
    59  				separated DATE NOT NULL DEFAULT '9999-12-31',
    60  				job_code INT,
    61  				store_id INT
    62  			)
    63  			PARTITION BY HASH(store_id)
    64  			PARTITIONS 4;`,
    65  
    66  		`CREATE TABLE t1 (
    67  			col1 INT NOT NULL,
    68  			col2 DATE NOT NULL,
    69  			col3 INT NOT NULL,
    70  			col4 INT NOT NULL,
    71  			PRIMARY KEY (col1, col2)
    72  		)
    73  		PARTITION BY HASH(col1)
    74  		PARTITIONS 4;`,
    75  
    76  		`CREATE TABLE t1 (
    77  			col1 INT NOT NULL,
    78  			col2 DATE NOT NULL,
    79  			col3 INT NOT NULL,
    80  			col4 INT NOT NULL,
    81  			PRIMARY KEY (col1, col3)
    82  		)
    83  		PARTITION BY HASH(col1 + col3)
    84  		PARTITIONS 4;`,
    85  
    86  		`CREATE TABLE t2 (
    87  			col1 INT NOT NULL,
    88  			col2 DATE NOT NULL,
    89  			col3 INT NOT NULL,
    90  			col4 INT NOT NULL,
    91  			PRIMARY KEY (col1)
    92  		)
    93  		PARTITION BY HASH(col1+10)
    94  		PARTITIONS 4;`,
    95  		`CREATE TABLE employees (
    96  				id INT NOT NULL,
    97  				fname VARCHAR(30),
    98  				lname VARCHAR(30),
    99  				hired DATE NOT NULL DEFAULT '1970-01-01',
   100  				separated DATE NOT NULL DEFAULT '9999-12-31',
   101  				job_code INT,
   102  				store_id INT
   103  			)
   104  			PARTITION BY LINEAR HASH( YEAR(hired) )
   105  			PARTITIONS 4;`,
   106  	}
   107  
   108  	mock := NewMockOptimizer(false)
   109  	for _, sql := range sqls {
   110  		t.Log(sql)
   111  		_, err := buildSingleStmt(mock, t, sql)
   112  		require.Nil(t, err)
   113  		if err != nil {
   114  			t.Fatalf("%+v", err)
   115  		}
   116  	}
   117  }
   118  
   119  func TestHashPartition2(t *testing.T) {
   120  	// HASH(expr) Partition
   121  	sqls := []string{
   122  		"CREATE TABLE t2 (col1 INT, col2 CHAR(5)) " +
   123  			"PARTITION BY HASH(col1) PARTITIONS 1 " +
   124  			"( PARTITION p0 " +
   125  			"ENGINE = 'engine_name' " +
   126  			"COMMENT = 'p0_comment' " +
   127  			"DATA DIRECTORY = 'data_dir' " +
   128  			"INDEX DIRECTORY = 'data_dir' " +
   129  			"MAX_ROWS = 100 " +
   130  			"MIN_ROWS = 100 " +
   131  			"TABLESPACE = space " +
   132  			"(SUBPARTITION sub_name) " +
   133  			");",
   134  	}
   135  
   136  	mock := NewMockOptimizer(false)
   137  	for _, sql := range sqls {
   138  		t.Log(sql)
   139  		_, err := buildSingleStmt(mock, t, sql)
   140  		require.Nil(t, err)
   141  		if err != nil {
   142  			t.Fatalf("%+v", err)
   143  		}
   144  	}
   145  }
   146  
   147  func TestHashPartitionError(t *testing.T) {
   148  	// HASH(expr) Partition
   149  	sqls := []string{
   150  		// In MySQL, RANGE, LIST, and HASH partitions require that the partitioning key must be of type INT or be returned through an expression.
   151  		// For the following Partition table test case, in matrixone, when the parameter of ceil function is of decimal type and the return value type is of decimal type,
   152  		// it cannot be used as the partition expression type, but in MySQL, when the parameter of ceil function is of decimal type and the return
   153  		// value is of int type, it can be used as the partition expression type
   154  		"create table p_hash_table_08(col1 tinyint,col2 varchar(30),col3 decimal(6,3))partition by hash(ceil(col3)) partitions 2;",
   155  		"CREATE TABLE t1 (col1 INT, col2 CHAR(5)) PARTITION BY HASH(col2);",
   156  		"CREATE TABLE t1 (col1 INT, col2 DECIMAL) PARTITION BY HASH(col2) PARTITIONS 4;",
   157  		"CREATE TABLE t1 (col1 INT, col2 DECIMAL) PARTITION BY HASH(12);",
   158  		"CREATE TABLE t1 (col1 INT, col2 CHAR(5), col3 DATETIME) PARTITION BY HASH (YEAR(col3)) PARTITIONS 4 SUBPARTITION BY KEY(col1);",
   159  		"CREATE TABLE t1 (col1 INT, col2 CHAR(5), col3 DATE) PARTITION BY HASH( YEAR(col3) ) PARTITIONS;",
   160  		"create table t3 (a int, b int) partition by hash(ceiling(a-b) + 23.5) partitions 10",
   161  		`CREATE TABLE employees (
   162  					id INT NOT NULL,
   163  					fname VARCHAR(30),
   164  					lname VARCHAR(30),
   165  					hired DATE NOT NULL DEFAULT '1970-01-01',
   166  					separated DATE NOT NULL DEFAULT '9999-12-31',
   167  					job_code INT,
   168  					store_id INT
   169  				)
   170  				PARTITION BY HASH(4)
   171  				PARTITIONS 4;`,
   172  
   173  		`CREATE TABLE t1 (
   174  			col1 INT NOT NULL,
   175  			col2 DATE NOT NULL,
   176  			col3 INT NOT NULL,
   177  			col4 INT NOT NULL,
   178  			PRIMARY KEY (col1, col2)
   179  		)
   180  			PARTITION BY HASH(col3)
   181  			PARTITIONS 4;`,
   182  
   183  		`CREATE TABLE t2 (
   184  			col1 INT NOT NULL,
   185  			col2 DATE NOT NULL,
   186  			col3 INT NOT NULL,
   187  			col4 INT NOT NULL,
   188  			PRIMARY KEY (col1)
   189  		)
   190  			PARTITION BY HASH(col1 + col3)
   191  			PARTITIONS 4;`,
   192  
   193  		`CREATE TABLE t2 (
   194  			col1 INT NOT NULL,
   195  			col2 DATE NOT NULL,
   196  			col3 INT NOT NULL,
   197  			col4 INT NOT NULL,
   198  			UNIQUE KEY (col1),
   199  			UNIQUE KEY (col3)
   200  		)
   201  		PARTITION BY HASH(col1+col3)
   202  		PARTITIONS 4;`,
   203  
   204  		`create table p_hash_table_03(
   205  			col1 bigint ,
   206  			col2 date default '1970-01-01',
   207  			col3 varchar(30)
   208  		)
   209  		partition by hash(year(col3))
   210  		partitions 8;`,
   211  
   212  		`CREATE TABLE employees (
   213  			id INT NOT NULL,
   214  			fname VARCHAR(30),
   215  			lname VARCHAR(30),
   216  			hired DATE NOT NULL DEFAULT '1970-01-01',
   217  			separated DATE NOT NULL DEFAULT '9999-12-31',
   218  			job_code INT,
   219  			store_id INT
   220  		) PARTITION BY HASH(store_id) PARTITIONS 102400000000;`,
   221  
   222  		`create table p_hash_table_03(
   223  				col1 bigint ,
   224  				col2 date default '1970-01-01',
   225  				col3 varchar(30)
   226  			)
   227  			partition by hash(col4)
   228  			partitions 8;`,
   229  	}
   230  
   231  	mock := NewMockOptimizer(false)
   232  	for _, sql := range sqls {
   233  		_, err := buildSingleStmt(mock, t, sql)
   234  		t.Log(sql)
   235  		require.NotNil(t, err)
   236  		t.Log(err)
   237  		if err == nil {
   238  			t.Fatalf("%+v", err)
   239  		}
   240  	}
   241  
   242  }
   243  
   244  func Test_hash_buildPartitionDefs(t *testing.T) {
   245  	type kase struct {
   246  		sql     string
   247  		def     *plan.PartitionByDef
   248  		wantErr bool
   249  	}
   250  
   251  	kases := []kase{
   252  		{
   253  			sql: "create table a(col1 int) partition by hash(col1) (partition x1, partition x2);",
   254  			def: &plan.PartitionByDef{
   255  				PartitionNum: 2,
   256  			},
   257  			wantErr: false,
   258  		},
   259  		{
   260  			sql: "create table a(col1 int) partition by hash(col1) (partition x1, partition x2);",
   261  			def: &plan.PartitionByDef{
   262  				PartitionNum: 1,
   263  			},
   264  			wantErr: true,
   265  		},
   266  		{
   267  			sql: "create table a(col1 int) partition by hash(col1) ;",
   268  			def: &plan.PartitionByDef{
   269  				PartitionNum: 2,
   270  			},
   271  			wantErr: false,
   272  		},
   273  		{
   274  			sql: "create table a(col1 int) partition by hash(col1) (partition x1, partition x1);",
   275  			def: &plan.PartitionByDef{
   276  				PartitionNum: 4,
   277  			},
   278  			wantErr: true,
   279  		},
   280  	}
   281  
   282  	hpb := &hashPartitionBuilder{}
   283  
   284  	for _, k := range kases {
   285  		one, err := parsers.ParseOne(context.TODO(), dialect.MYSQL, k.sql, 1, 0)
   286  		require.Nil(t, err)
   287  		syntaxDefs := one.(*tree.CreateTable).PartitionOption.Partitions
   288  		err = hpb.buildPartitionDefs(context.TODO(), nil, k.def, syntaxDefs)
   289  		fmt.Println(k.sql)
   290  		if !k.wantErr {
   291  			require.Nil(t, err)
   292  			require.LessOrEqual(t, len(syntaxDefs), int(k.def.PartitionNum))
   293  			require.Equal(t, int(k.def.PartitionNum), len(k.def.Partitions))
   294  			//check partition names
   295  			i := 0
   296  			for ; i < len(syntaxDefs); i++ {
   297  				require.Equal(t, string(syntaxDefs[i].Name), k.def.Partitions[i].PartitionName)
   298  				require.Equal(t, i, int(k.def.Partitions[i].OrdinalPosition)-1)
   299  			}
   300  			for ; i < int(k.def.PartitionNum); i++ {
   301  				require.Equal(t, fmt.Sprintf("p%d", i), k.def.Partitions[i].PartitionName)
   302  				require.Equal(t, i, int(k.def.Partitions[i].OrdinalPosition)-1)
   303  			}
   304  		} else {
   305  			require.NotNil(t, err)
   306  		}
   307  
   308  	}
   309  
   310  }
   311  
   312  func Test_hash_buildEvalPartitionExpression(t *testing.T) {
   313  	sql1 := " create table a(col1 int,col2 int) partition by hash(col1+col2)"
   314  	one, err := parsers.ParseOne(context.TODO(), dialect.MYSQL, sql1, 1, 0)
   315  	require.Nil(t, err)
   316  
   317  	/*
   318  		table test:
   319  		col1 int32 pk
   320  		col2 int32
   321  	*/
   322  	tableDef := &plan.TableDef{
   323  		Name: "a",
   324  		Pkey: &plan.PrimaryKeyDef{
   325  			Names: []string{"col1"},
   326  		},
   327  	}
   328  
   329  	addCol(tableDef, &ColDef{
   330  		Name: "col1",
   331  		Typ: plan.Type{
   332  			Id: int32(types.T_int8),
   333  		},
   334  	})
   335  	addCol(tableDef, &ColDef{
   336  		Name: "col2",
   337  		Typ: plan.Type{
   338  			Id: int32(types.T_int8),
   339  		},
   340  	})
   341  	//partition keys [col1]
   342  	pb, err := mockPartitionBinder(tableDef)
   343  	require.Nil(t, err)
   344  
   345  	partDef := &PartitionByDef{}
   346  
   347  	hpb := &hashPartitionBuilder{}
   348  	err = hpb.buildEvalPartitionExpression(context.TODO(), pb, one.(*tree.CreateTable).PartitionOption, partDef)
   349  	require.Nil(t, err)
   350  	require.NotNil(t, partDef.PartitionExpression)
   351  }