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 }