github.com/matrixorigin/matrixone@v1.2.0/pkg/sql/plan/partition.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 catalog2 "github.com/matrixorigin/matrixone/pkg/catalog" 21 "go/constant" 22 "strings" 23 24 "github.com/matrixorigin/matrixone/pkg/common/moerr" 25 "github.com/matrixorigin/matrixone/pkg/container/types" 26 "github.com/matrixorigin/matrixone/pkg/pb/plan" 27 "github.com/matrixorigin/matrixone/pkg/sql/parsers/dialect" 28 "github.com/matrixorigin/matrixone/pkg/sql/parsers/tree" 29 ) 30 31 const ( 32 /* 33 https://dev.mysql.com/doc/refman/8.0/en/create-table.html 34 PARTITION BY 35 If used, a partition_options clause begins with PARTITION BY. This clause contains the function that is used 36 to determine the partition; the function returns an integer value ranging from 1 to num, where num is 37 the number of partitions. (The maximum number of user-defined partitions which a table may contain is 1024; 38 the number of subpartitions—discussed later in this section—is included in this maximum.) 39 */ 40 PartitionCountLimit = 1024 41 42 /* 43 https://dev.mysql.com/doc/refman/8.0/en/create-table.html 44 1. KEY(column_list): the column_list argument is simply a list of 1 or more table columns (maximum: 16). 45 2. RANGE COLUMNS(column_list): The maximum number of columns that can be referenced in the column_list 46 and value_list is 16. 47 3. LIST COLUMNS(column_list): The maximum number of columns that can be used in the column_list and 48 in the elements making up the value_list is 16. 49 */ 50 PartitionColumnsLimit = 16 51 ) 52 53 type partitionBuilder interface { 54 build(ctx context.Context, partitionBinder *PartitionBinder, stmt *tree.CreateTable, tableDef *TableDef) error 55 56 // buildPartitionDefinitionsInfo build partition definitions info without assign partition id. 57 buildPartitionDefs(ctx context.Context, partitionBinder *PartitionBinder, partitionDef *plan.PartitionByDef, defs []*tree.Partition) (err error) 58 59 // checkTableDefPartition Perform integrity constraint check on partitions of create table statement 60 checkPartitionIntegrity(ctx context.Context, partitionBinder *PartitionBinder, tableDef *TableDef, partitionDef *plan.PartitionByDef) error 61 62 // This method is used to convert different types of partition structures into plan.Expr 63 buildEvalPartitionExpression(ctx context.Context, partitionBinder *PartitionBinder, stmt *tree.PartitionOption, partitionDef *plan.PartitionByDef) error 64 } 65 66 var _ partitionBuilder = &hashPartitionBuilder{} 67 var _ partitionBuilder = &keyPartitionBuilder{} 68 var _ partitionBuilder = &rangePartitionBuilder{} 69 var _ partitionBuilder = &listPartitionBuilder{} 70 71 // visit partition expression and check the correctness of partition expression 72 type partitionExprProcessor func(ctx context.Context, tbInfo *plan.TableDef, expr tree.Expr) error 73 74 // partitionExprChecker used to check partition expression legitimacy 75 type partitionExprChecker struct { 76 ctx context.Context 77 processors []partitionExprProcessor 78 tableInfo *plan.TableDef 79 err error // Error occurred during checking partition expression 80 columns []*ColDef // Columns used in partition expressions 81 } 82 83 func newPartitionExprChecker(ctx context.Context, tbInfo *plan.TableDef, processor ...partitionExprProcessor) *partitionExprChecker { 84 p := &partitionExprChecker{ 85 ctx: ctx, 86 processors: processor, 87 tableInfo: tbInfo, 88 } 89 p.processors = append(p.processors, p.extractColumns) 90 return p 91 } 92 93 func (p *partitionExprChecker) Enter(n tree.Expr) (node tree.Expr, skipChildren bool) { 94 for _, processor := range p.processors { 95 if err := processor(p.ctx, p.tableInfo, n); err != nil { 96 p.err = err 97 return n, true 98 } 99 } 100 return n, false 101 } 102 103 func (p *partitionExprChecker) Exit(n tree.Expr) (node tree.Expr, ok bool) { 104 return n, p.err == nil 105 } 106 107 func (p *partitionExprChecker) extractColumns(ctx context.Context, _ *plan.TableDef, expr tree.Expr) error { 108 columnNameExpr, ok := expr.(*tree.UnresolvedName) 109 if !ok { 110 return nil 111 } 112 113 colInfo := findColumnByName(columnNameExpr.Parts[0], p.tableInfo) 114 if colInfo == nil { 115 return moerr.NewBadFieldError(ctx, columnNameExpr.Parts[0], "partition function") 116 } 117 118 p.columns = append(p.columns, colInfo) 119 return nil 120 } 121 122 // checkPartitionExprValid checks partition expression function validly. 123 func checkPartitionExprValid(ctx context.Context, tblInfo *plan.TableDef, expr tree.Expr) error { 124 if expr == nil { 125 return nil 126 } 127 exprChecker := newPartitionExprChecker(ctx, tblInfo, checkPartitionExprArgs, checkPartitionExprAllowed) 128 expr.Accept(exprChecker) 129 if exprChecker.err != nil { 130 return exprChecker.err 131 } 132 if len(exprChecker.columns) == 0 { 133 return moerr.NewWrongExprInPartitionFunc(ctx) 134 } 135 return nil 136 } 137 138 // buildPartitionExpr enables the use of multiple columns in partitioning keys 139 func buildPartitionExpr(ctx context.Context, tblInfo *plan.TableDef, partitionBinder *PartitionBinder, partitionDef *plan.PartitionByDef, pExpr tree.Expr) error { 140 if err := checkPartitionExprValid(ctx, tblInfo, pExpr); err != nil { 141 return err 142 } 143 planExpr, err := partitionBinder.BindExpr(pExpr, 0, true) 144 if err != nil { 145 return err 146 } 147 //TODO: format partition expression 148 //fmtCtx := tree.NewFmtCtxWithFlag(dialect.MYSQL, tree.RestoreNameBackQuotes) 149 //pExpr.Format(fmtCtx) 150 //exprFmtStr := fmtCtx.ToString() 151 152 // Temporary operation 153 partitionDef.PartitionExpr = &plan.PartitionExpr{ 154 Expr: planExpr, 155 ExprStr: tree.String(pExpr, dialect.MYSQL), 156 //ExprFmtStr: exprFmtStr, 157 } 158 return nil 159 } 160 161 // getValidPartitionCount checks the subpartition and adjust the number of the partition 162 func getValidPartitionCount(ctx context.Context, needPartitionDefs bool, partitionSyntaxDef *tree.PartitionOption) (uint64, error) { 163 var err error 164 //step 1 : reject subpartition 165 if partitionSyntaxDef.SubPartBy != nil { 166 return 0, moerr.NewInvalidInput(ctx, "subpartition is unsupported") 167 } 168 169 if needPartitionDefs && len(partitionSyntaxDef.Partitions) == 0 { 170 if _, ok := partitionSyntaxDef.PartBy.PType.(*tree.ListType); ok { 171 return 0, moerr.NewPartitionsMustBeDefined(ctx, "LIST") 172 } else { 173 return 0, moerr.NewInvalidInput(ctx, "each partition must be defined") 174 } 175 } 176 177 //step 2: verify the partition number [1,1024] 178 partitionCount := partitionSyntaxDef.PartBy.Num 179 /* 180 "partitionCount = 0" only occurs when the PARTITIONS clause is missed. 181 */ 182 if partitionCount <= 0 { 183 if len(partitionSyntaxDef.Partitions) == 0 { 184 //if there is no partition definition, the default number for the partitionCount is 1. 185 partitionCount = 1 186 } else { 187 //if there are at lease one partition definitions, the default number for the partitionsNums 188 //is designated as the number of the partition definitions. 189 partitionCount = uint64(len(partitionSyntaxDef.Partitions)) 190 } 191 } else if len(partitionSyntaxDef.Partitions) != 0 && partitionCount != uint64(len(partitionSyntaxDef.Partitions)) { 192 //if partition definitions exists in the syntax, but the count of it is different from 193 //the one in PARTITIONS clause, it is wrong. 194 return 0, moerr.NewInvalidInput(ctx, "Wrong number of partitions defined") 195 } 196 // check partition number 197 if err = checkPartitionCount(ctx, int(partitionCount)); err != nil { 198 return 0, err 199 } 200 return partitionCount, err 201 } 202 203 // buildPartitionColumns enables the use of multiple columns in partitioning keys 204 func buildPartitionColumns(ctx context.Context, tableDef *TableDef, partitionBinder *PartitionBinder, partitionDef *plan.PartitionByDef, columnList []*tree.UnresolvedName) error { 205 var err error 206 partitionDef.PartitionColumns = &plan.PartitionColumns{ 207 Columns: make([]*plan.Expr, len(columnList)), 208 PartitionColumns: make([]string, len(columnList)), 209 } 210 211 // 1. First, check if the partition column is legal, 212 for i, column := range columnList { 213 partitionDef.PartitionColumns.PartitionColumns[i] = tree.String(column, dialect.MYSQL) 214 } 215 if err = checkColumnsPartitionType(ctx, tableDef, partitionDef); err != nil { 216 return err 217 } 218 219 // 2. then construct the expression for the partition column 220 for i, column := range columnList { 221 partitionDef.PartitionColumns.Columns[i], err = partitionBinder.BindColRef(column, 0, true) 222 if err != nil { 223 return err 224 } 225 } 226 return nil 227 } 228 229 // checkColumnsPartitionType Check if the type of the partition column is correct 230 func checkColumnsPartitionType(ctx context.Context, tbInfo *TableDef, pi *plan.PartitionByDef) error { 231 for _, colName := range pi.PartitionColumns.PartitionColumns { 232 colInfo := findColumnByName(colName, tbInfo) 233 if colInfo == nil { 234 return moerr.NewFieldNotFoundPart(ctx) 235 } 236 237 t := types.T(colInfo.Typ.Id) 238 if pi.Type == plan.PartitionType_KEY || pi.Type == plan.PartitionType_LINEAR_KEY { 239 // When partitioning by [LINEAR] KEY, it is possible to use columns of any valid MySQL data type other than TEXT or BLOB 240 // as partitioning keys, because MySQL's internal key-hashing functions produce the correct data type from these types. 241 // See https://dev.mysql.com/doc/refman/8.0/en/partitioning-limitations.html 242 if t == types.T_blob || t == types.T_text || t == types.T_json { 243 return moerr.NewBlobFieldInPartFunc(ctx) 244 } 245 } else { 246 // Both `RANGE COLUMNS` partitioning and `LIST COLUMNS` partitioning support the use of non-integer columns for defining value ranges or list members. The permitted data types are shown in the following list: 247 //1. All integer types: TINYINT, SMALLINT, MEDIUMINT, INT (INTEGER), and BIGINT. (This is the same as with partitioning by RANGE and LIST.) 248 // Other numeric data types (such as DECIMAL or FLOAT) are not supported as partitioning columns. 249 //2. DATE and DATETIME. 250 // Columns using other data types relating to dates or times are not supported as partitioning columns. 251 //3. The following string types: CHAR, VARCHAR, BINARY, and VARBINARY. 252 // TEXT and BLOB columns are not supported as partitioning columns. 253 // See https://dev.mysql.com/doc/refman/8.0/en/partitioning-columns.html 254 if t == types.T_float32 || t == types.T_float64 || t == types.T_decimal64 || t == types.T_decimal128 || 255 t == types.T_timestamp || t == types.T_blob || t == types.T_text || t == types.T_json || t == types.T_enum { 256 return moerr.NewFieldTypeNotAllowedAsPartitionField(ctx, colName) 257 } 258 } 259 } 260 return nil 261 } 262 263 // buildPartitionDefs constructs the partitions 264 func buildPartitionDefs(ctx context.Context, 265 partitionDef *plan.PartitionByDef, syntaxDefs []*tree.Partition) error { 266 if len(syntaxDefs) != 0 && len(syntaxDefs) != int(partitionDef.PartitionNum) { 267 return moerr.NewInvalidInput(ctx, "Wrong number of partitions defined") 268 } 269 dedup := make(map[string]bool) 270 if len(syntaxDefs) == 0 { 271 //complement partition defs missing in syntax 272 for i := 0; i < int(partitionDef.PartitionNum); i++ { 273 name := fmt.Sprintf("p%d", i) 274 if _, ok := dedup[name]; ok { 275 //return moerr.NewInvalidInput(ctx, "duplicate partition name %s", name) 276 return moerr.NewSameNamePartition(ctx, name) 277 } 278 dedup[name] = true 279 pi := &plan.PartitionItem{ 280 PartitionName: name, 281 OrdinalPosition: uint32(i + 1), 282 } 283 partitionDef.Partitions = append(partitionDef.Partitions, pi) 284 } 285 } else { 286 //process defs in syntax 287 for i := 0; i < len(syntaxDefs); i++ { 288 name := string(syntaxDefs[i].Name) 289 if _, ok := dedup[name]; ok { 290 //return moerr.NewInvalidInput(ctx, "duplicate partition name %s", name) 291 return moerr.NewSameNamePartition(ctx, name) 292 } 293 dedup[name] = true 294 295 //get COMMENT option only 296 comment := "" 297 for _, option := range syntaxDefs[i].Options { 298 if commentOpt, ok := option.(*tree.TableOptionComment); ok { 299 comment = commentOpt.Comment 300 } 301 } 302 303 pi := &plan.PartitionItem{ 304 PartitionName: name, 305 OrdinalPosition: uint32(i + 1), 306 Comment: comment, 307 } 308 partitionDef.Partitions = append(partitionDef.Partitions, pi) 309 } 310 } 311 312 return nil 313 } 314 315 func checkPartitionIntegrity(ctx context.Context, partitionBinder *PartitionBinder, tableDef *TableDef, partitionDef *plan.PartitionByDef) error { 316 if err := checkPartitionKeys(ctx, partitionBinder.builder.nameByColRef, tableDef, partitionDef); err != nil { 317 return err 318 } 319 if err := checkPartitionExprType(ctx, partitionBinder, tableDef, partitionDef); err != nil { 320 return err 321 } 322 if err := checkPartitionDefines(ctx, partitionBinder, partitionDef, tableDef); err != nil { 323 return err 324 } 325 return nil 326 } 327 328 func getPrimaryKeyAndUniqueKey(defs tree.TableDefs) (primaryKeys []*tree.UnresolvedName, uniqueIndexs []*tree.UniqueIndex) { 329 for _, item := range defs { 330 switch def := item.(type) { 331 case *tree.ColumnTableDef: 332 for _, attr := range def.Attributes { 333 if _, ok := attr.(*tree.AttributePrimaryKey); ok { 334 primaryKeys = append(primaryKeys, def.Name) 335 } 336 337 if _, ok := attr.(*tree.AttributeUniqueKey); ok { 338 part := &tree.KeyPart{ 339 ColName: def.Name, 340 } 341 uniqueKey := &tree.UniqueIndex{ 342 KeyParts: []*tree.KeyPart{part}, 343 Name: "", 344 Empty: true, 345 } 346 uniqueIndexs = append(uniqueIndexs, uniqueKey) 347 } 348 } 349 case *tree.PrimaryKeyIndex: 350 for _, key := range def.KeyParts { 351 primaryKeys = append(primaryKeys, key.ColName) 352 } 353 case *tree.UniqueIndex: 354 uniqueIndexs = append(uniqueIndexs, def) 355 } 356 } 357 return 358 } 359 360 // This method is used to generate partition ast for key partition and hash partition 361 // For example: abs (hash_value (col3))% 4 362 func genPartitionAst(exprs tree.Exprs, partNum int64) tree.Expr { 363 hashFuncName := tree.SetUnresolvedName(strings.ToLower("hash_value")) 364 hashfuncExpr := &tree.FuncExpr{ 365 Func: tree.FuncName2ResolvableFunctionReference(hashFuncName), 366 Exprs: exprs, 367 } 368 369 absFuncName := tree.SetUnresolvedName(strings.ToLower("abs")) 370 absFuncExpr := &tree.FuncExpr{ 371 Func: tree.FuncName2ResolvableFunctionReference(absFuncName), 372 Exprs: tree.Exprs{hashfuncExpr}, 373 } 374 375 numstr := fmt.Sprintf("%v", partNum) 376 divExpr := tree.NewNumValWithType(constant.MakeInt64(partNum), numstr, false, tree.P_int64) 377 modOpExpr := tree.NewBinaryExpr(tree.MOD, absFuncExpr, divExpr) 378 return modOpExpr 379 } 380 381 // This method is used to convert the list columns partition into case when expression,such as: 382 // PARTITION BY LIST COLUMNS(a,b) ( 383 // PARTITION p0 VALUES IN( (0,0), (NULL,NULL) ), 384 // PARTITION p1 VALUES IN( (0,1), (0,2) ), 385 // PARTITION p2 VALUES IN( (1,0), (2,0) ) 386 // );--> 387 // case 388 // when a = 0 and b = 0 or a = null and b = null then 0 389 // when a = 0 and b = 1 or a = 0 and b = 2 then 1 390 // when a = 1 and b = 0 or a = 2 and b = 0 then 2 391 // else -1 392 // end 393 func buildListColumnsCaseWhenExpr(columnsExpr []*tree.UnresolvedName, defs []*tree.Partition) (*tree.CaseExpr, error) { 394 whens := make([]*tree.When, len(defs)) 395 396 for i, partition := range defs { 397 valuesIn := partition.Values.(*tree.ValuesIn) 398 399 elements := make([]tree.Expr, len(valuesIn.ValueList)) 400 for j, value := range valuesIn.ValueList { 401 if tuple, ok := value.(*tree.Tuple); ok { 402 exprs := tuple.Exprs 403 if len(exprs) != len(columnsExpr) { 404 panic("the number of IN expression parameters does not match") 405 } 406 407 if len(columnsExpr) == 1 { 408 newExpr := tree.NewComparisonExpr(tree.EQUAL, columnsExpr[0], exprs[0]) 409 elements[j] = newExpr 410 continue 411 } 412 413 if len(columnsExpr) >= 2 { 414 var andExpr tree.Expr 415 416 first := true 417 for k, lexpr := range columnsExpr { 418 if first { 419 andExpr = tree.NewComparisonExpr(tree.EQUAL, lexpr, exprs[k]) 420 first = false 421 continue 422 } 423 newExpr := tree.NewComparisonExpr(tree.EQUAL, lexpr, exprs[k]) 424 andExpr = tree.NewAndExpr(andExpr, newExpr) 425 } 426 elements[j] = andExpr 427 continue 428 } 429 } else { 430 if len(columnsExpr) != 1 { 431 panic("the number of IN expression parameters does not match") 432 } 433 newExpr := tree.NewComparisonExpr(tree.EQUAL, columnsExpr[0], value) 434 elements[j] = newExpr 435 continue 436 } 437 } 438 439 var conditionExpr tree.Expr 440 if len(valuesIn.ValueList) == 1 { 441 conditionExpr = elements[0] 442 } 443 444 if len(valuesIn.ValueList) > 1 { 445 for m := 1; m < len(elements); m++ { 446 if m == 1 { 447 conditionExpr = tree.NewOrExpr(elements[m-1], elements[m]) 448 } else { 449 conditionExpr = tree.NewOrExpr(conditionExpr, elements[m]) 450 } 451 } 452 } 453 454 when := &tree.When{ 455 Cond: conditionExpr, 456 Val: tree.NewNumValWithType(constant.MakeInt64(int64(i)), fmt.Sprintf("%v", i), false, tree.P_int64), 457 } 458 whens[i] = when 459 } 460 caseWhenExpr := &tree.CaseExpr{ 461 Expr: nil, 462 Whens: whens, 463 Else: tree.NewNumValWithType(constant.MakeInt64(int64(-1)), fmt.Sprintf("%v", -1), false, tree.P_int64), 464 } 465 return caseWhenExpr, nil 466 } 467 468 // This method is used to convert the range partition into case when expression,such as: 469 // PARTITION BY RANGE (code + 5) ( 470 // PARTITION p0 VALUES LESS THAN (6), 471 // PARTITION p1 VALUES LESS THAN (11), 472 // PARTITION p2 VALUES LESS THAN (MAXVALUE), 473 // ); --> 474 // case when (code + 5) < 6 then 0 when (code + 5) < 11 then 1 when true then 3 else -1 end 475 func buildRangeCaseWhenExpr(pexpr tree.Expr, defs []*tree.Partition) (*tree.CaseExpr, error) { 476 whens := make([]*tree.When, len(defs)) 477 for i, partition := range defs { 478 valuesLessThan := partition.Values.(*tree.ValuesLessThan) 479 if len(valuesLessThan.ValueList) != 1 { 480 panic("range partition less than expression should have one element") 481 } 482 valueExpr := valuesLessThan.ValueList[0] 483 484 var conditionExpr tree.Expr 485 if _, ok := valueExpr.(*tree.MaxValue); ok { 486 conditionExpr = tree.NewNumValWithType(constant.MakeBool(true), "true", false, tree.P_bool) 487 } else { 488 LessThanExpr := tree.NewComparisonExpr(tree.LESS_THAN, pexpr, valueExpr) 489 conditionExpr = LessThanExpr 490 } 491 492 when := &tree.When{ 493 Cond: conditionExpr, 494 Val: tree.NewNumValWithType(constant.MakeInt64(int64(i)), fmt.Sprintf("%v", i), false, tree.P_int64), 495 } 496 whens[i] = when 497 } 498 499 caseWhenExpr := &tree.CaseExpr{ 500 Expr: nil, 501 Whens: whens, 502 Else: tree.NewNumValWithType(constant.MakeInt64(int64(-1)), fmt.Sprintf("%v", -1), false, tree.P_int64), 503 } 504 return caseWhenExpr, nil 505 } 506 507 // This method is used to optimize the row constructor expression in range columns partition item into a common logical operation expression, 508 // such as: (a, b, c) < (x0, x1, x2) -> a < x0 || (a = x0 && (b < x1 || b = x1 && c < x2)) 509 func buildRangeColumnsCaseWhenExpr(columnsExpr []*tree.UnresolvedName, defs []*tree.Partition) (*tree.CaseExpr, error) { 510 whens := make([]*tree.When, len(defs)) 511 for i, partition := range defs { 512 valuesLessThan := partition.Values.(*tree.ValuesLessThan) 513 514 if len(valuesLessThan.ValueList) != len(columnsExpr) { 515 panic("the number of less value expression parameters does not match") 516 } 517 518 var tempExpr tree.Expr 519 for j := len(valuesLessThan.ValueList) - 1; j >= 0; j-- { 520 valueExpr := valuesLessThan.ValueList[j] 521 if j == len(valuesLessThan.ValueList)-1 { 522 if _, ok := valueExpr.(*tree.MaxValue); ok { 523 trueExpr := tree.NewNumValWithType(constant.MakeBool(true), "true", false, tree.P_bool) 524 tempExpr = trueExpr 525 } else { 526 lessThanExpr := tree.NewComparisonExpr(tree.LESS_THAN, columnsExpr[j], valueExpr) 527 tempExpr = lessThanExpr 528 } 529 continue 530 } else { 531 var firstExpr tree.Expr 532 if _, ok := valueExpr.(*tree.MaxValue); ok { 533 trueExpr := tree.NewNumValWithType(constant.MakeBool(true), "true", false, tree.P_bool) 534 firstExpr = trueExpr 535 } else { 536 lessThanExpr := tree.NewComparisonExpr(tree.LESS_THAN, columnsExpr[j], valueExpr) 537 firstExpr = lessThanExpr 538 } 539 540 var middleExpr tree.Expr 541 if _, ok := valueExpr.(*tree.MaxValue); ok { 542 trueExpr := tree.NewNumValWithType(constant.MakeBool(true), "true", false, tree.P_bool) 543 middleExpr = trueExpr 544 } else { 545 equalExpr := tree.NewComparisonExpr(tree.EQUAL, columnsExpr[j], valueExpr) 546 middleExpr = equalExpr 547 } 548 secondExpr := tree.NewAndExpr(middleExpr, tempExpr) 549 tempExpr = tree.NewOrExpr(firstExpr, secondExpr) 550 } 551 } 552 553 when := &tree.When{ 554 Cond: tempExpr, 555 Val: tree.NewNumValWithType(constant.MakeInt64(int64(i)), fmt.Sprintf("%v", i), false, tree.P_int64), 556 } 557 whens[i] = when 558 } 559 caseWhenExpr := &tree.CaseExpr{ 560 Expr: nil, 561 Whens: whens, 562 Else: tree.NewNumValWithType(constant.MakeInt64(int64(-1)), fmt.Sprintf("%v", -1), false, tree.P_int64), 563 } 564 return caseWhenExpr, nil 565 } 566 567 // This method is used to convert the list columns partition into an case when expression,such as: 568 // PARTITION BY LIST (expr) ( 569 // PARTITION p0 VALUES IN(1, 5, 9, 13, 17), 570 // PARTITION p1 VALUES IN (2, 6, 10, 14, 18) 571 // );--> 572 // case when expr in (1, 5, 9, 13, 17) then 0 when expr in (2, 6, 10, 14, 18) then 1 else -1 end 573 func buildListCaseWhenExpr(listExpr tree.Expr, defs []*tree.Partition) (*tree.CaseExpr, error) { 574 whens := make([]*tree.When, len(defs)) 575 for i, partition := range defs { 576 valuesIn := partition.Values.(*tree.ValuesIn) 577 578 tuple := tree.NewTuple(valuesIn.ValueList) 579 inExpr := tree.NewComparisonExpr(tree.IN, listExpr, tuple) 580 581 when := &tree.When{ 582 Cond: inExpr, 583 Val: tree.NewNumValWithType(constant.MakeInt64(int64(i)), fmt.Sprintf("%v", i), false, tree.P_int64), 584 } 585 whens[i] = when 586 } 587 caseWhenExpr := &tree.CaseExpr{ 588 Expr: nil, 589 Whens: whens, 590 Else: tree.NewNumValWithType(constant.MakeInt64(int64(-1)), fmt.Sprintf("%v", -1), false, tree.P_int64), 591 } 592 return caseWhenExpr, nil 593 } 594 595 // checkPartitionExprType checks partition function return type. 596 func checkPartitionExprType(ctx context.Context, _ *PartitionBinder, _ *TableDef, partitionDef *plan.PartitionByDef) error { 597 if partitionDef.PartitionExpr != nil && partitionDef.PartitionExpr.Expr != nil { 598 expr := partitionDef.PartitionExpr.Expr 599 t := types.T(expr.Typ.Id) 600 if partitionDef.Type == plan.PartitionType_HASH || partitionDef.Type == plan.PartitionType_LINEAR_HASH { 601 if !t.IsInteger() { 602 if _, ok := expr.Expr.(*plan.Expr_Col); ok { 603 return moerr.NewFieldTypeNotAllowedAsPartitionField(ctx, partitionDef.PartitionExpr.ExprStr) 604 } else { 605 return moerr.NewPartitionFuncNotAllowed(ctx, "PARTITION") 606 } 607 } 608 } 609 610 if partitionDef.Type == plan.PartitionType_RANGE && !t.IsInteger() { 611 return moerr.NewFieldTypeNotAllowedAsPartitionField(ctx, partitionDef.PartitionExpr.ExprStr) 612 } 613 614 if partitionDef.Type == plan.PartitionType_LIST { 615 if !t.IsInteger() { 616 if _, ok := expr.Expr.(*plan.Expr_Col); ok { 617 return moerr.NewFieldTypeNotAllowedAsPartitionField(ctx, partitionDef.PartitionExpr.ExprStr) 618 } else { 619 return moerr.NewPartitionFuncNotAllowed(ctx, "PARTITION") 620 } 621 } 622 } 623 } 624 return nil 625 } 626 627 // stringSliceToMap converts the string slice to the string map 628 // return true -- has duplicate names 629 func stringSliceToMap(stringSlice []string, stringMap map[string]int) (bool, string) { 630 for _, s := range stringSlice { 631 if _, ok := stringMap[s]; ok { 632 return true, s 633 } 634 stringMap[s] = 0 635 } 636 return false, "" 637 } 638 639 func stringSliceToMapForIndexParts(stringSlice []string, stringMap map[string]int) (bool, string) { 640 for _, s := range stringSlice { 641 s = catalog2.ResolveAlias(s) 642 if _, ok := stringMap[s]; ok { 643 return true, s 644 } 645 stringMap[s] = 0 646 } 647 return false, "" 648 } 649 650 // checkPartitionKeys checks the partitioning key is included in the table constraint. 651 func checkPartitionKeys(ctx context.Context, nameByColRef map[[2]int32]string, 652 tableDef *TableDef, partitionDef *plan.PartitionByDef) error { 653 partitionKeys := make(map[string]int) 654 if partitionDef.PartitionColumns != nil { 655 if dup, dupName := stringSliceToMap(partitionDef.PartitionColumns.PartitionColumns, partitionKeys); dup { 656 //return moerr.NewInvalidInput(ctx, "duplicate name %s", dupName) 657 return moerr.NewSameNamePartition(ctx, dupName) 658 } 659 } else if partitionDef.PartitionExpr.Expr != nil { 660 extractColFromExpr(nameByColRef, partitionDef.PartitionExpr.Expr, partitionKeys) 661 } else { 662 return moerr.NewInvalidInput(ctx, "both COLUMNS and EXPR in PARTITION BY are invalid") 663 } 664 665 //do nothing 666 if len(partitionKeys) == 0 { 667 return nil 668 } 669 670 if tableDef.Pkey != nil && !onlyHasHiddenPrimaryKey(tableDef) { 671 pKeys := make(map[string]int) 672 if dup, dupName := stringSliceToMap(tableDef.Pkey.Names, pKeys); dup { 673 //return moerr.NewInvalidInput(ctx, "duplicate name %s", dupName) 674 return moerr.NewSameNamePartition(ctx, dupName) 675 } 676 if !checkUniqueKeyIncludePartKey(partitionKeys, pKeys) { 677 //return moerr.NewInvalidInput(ctx, "partition key is not part of primary key") 678 return moerr.NewUniqueKeyNeedAllFieldsInPf(ctx, "PRIMARY KEY") 679 } 680 } 681 682 if tableDef.Indexes != nil { 683 for _, indexDef := range tableDef.Indexes { 684 if indexDef.Unique { 685 uniqueKeys := make(map[string]int) 686 if dup, dupName := stringSliceToMapForIndexParts(indexDef.Parts, uniqueKeys); dup { 687 //return moerr.NewInvalidInput(ctx, "duplicate name %s", dupName) 688 return moerr.NewSameNamePartition(ctx, dupName) 689 } 690 if !checkUniqueKeyIncludePartKey(partitionKeys, uniqueKeys) { 691 //return moerr.NewInvalidInput(ctx, "partition key is not part of unique key") 692 return moerr.NewUniqueKeyNeedAllFieldsInPf(ctx, "PRIMARY KEY") 693 } 694 } 695 } 696 } 697 698 return nil 699 } 700 701 // checkUniqueKeyIncludePartKey checks the partitioning key is included in the constraint(primary key and unique key). 702 func checkUniqueKeyIncludePartKey(partitionKeys map[string]int, uqkeys map[string]int) bool { 703 for key := range partitionKeys { 704 if !findColumnInIndexCols(key, uqkeys) { 705 return false 706 } 707 } 708 return true 709 } 710 711 func findColumnInIndexCols(c string, pkcols map[string]int) bool { 712 for c1 := range pkcols { 713 if strings.EqualFold(c, c1) { 714 return true 715 } 716 } 717 return false 718 } 719 720 /* 721 checkPartitionDefines Check partition definition 722 723 1.check partition name unique or not 724 2.check partition count limitation 725 3.check partition columns count limitation 726 4.check partition column name uinque 727 */ 728 func checkPartitionDefines(ctx context.Context, partitionBinder *PartitionBinder, partitionDef *plan.PartitionByDef, tableDef *TableDef) error { 729 var err error 730 if err = checkPartitionNameUnique(ctx, partitionDef); err != nil { 731 return err 732 } 733 734 if err = checkPartitionCount(ctx, len(partitionDef.Partitions)); err != nil { 735 return err 736 } 737 738 if err = checkPartitionColumnsCount(ctx, partitionDef); err != nil { 739 return err 740 } 741 742 if err = checkPartitionColumnsUnique(ctx, partitionDef); err != nil { 743 return err 744 } 745 746 if len(partitionDef.Partitions) == 0 { 747 if partitionDef.Type == plan.PartitionType_RANGE || partitionDef.Type == plan.PartitionType_RANGE_COLUMNS { 748 return moerr.NewPartitionsMustBeDefined(ctx, "RANGE") 749 } else if partitionDef.Type == plan.PartitionType_LIST || partitionDef.Type == plan.PartitionType_LIST_COLUMNS { 750 return moerr.NewPartitionsMustBeDefined(ctx, "LIST") 751 } 752 } 753 754 switch partitionDef.Type { 755 case plan.PartitionType_RANGE, plan.PartitionType_RANGE_COLUMNS: 756 err = checkPartitionByRange(partitionBinder, partitionDef, tableDef) 757 case plan.PartitionType_LIST, plan.PartitionType_LIST_COLUMNS: 758 err = checkPartitionByList(partitionBinder, partitionDef, tableDef) 759 } 760 return err 761 } 762 763 // checkPartitionColumnsUnique check duplicate partition columns 764 func checkPartitionColumnsUnique(ctx context.Context, pi *plan.PartitionByDef) error { 765 if pi.PartitionColumns != nil { 766 if len(pi.PartitionColumns.PartitionColumns) <= 1 { 767 return nil 768 } 769 var columnsMap = make(map[string]struct{}) 770 for _, column := range pi.PartitionColumns.PartitionColumns { 771 if _, ok := columnsMap[column]; ok { 772 return moerr.NewSameNamePartitionField(ctx, column) 773 } 774 columnsMap[column] = struct{}{} 775 } 776 } 777 return nil 778 } 779 780 // checkPartitionColumns Check if the number of partition columns exceeds the limit 781 func checkPartitionColumnsCount(ctx context.Context, pi *plan.PartitionByDef) error { 782 if pi.PartitionColumns != nil && len(pi.PartitionColumns.PartitionColumns) > PartitionColumnsLimit { 783 return moerr.NewErrTooManyPartitionFuncFields(ctx, "list of partition fields") 784 } 785 return nil 786 } 787 788 // checkPartitionCount: check whether check partition number exceeds the limit 789 func checkPartitionCount(ctx context.Context, partNum int) error { 790 if partNum > PartitionCountLimit { 791 return moerr.NewErrTooManyPartitions(ctx) 792 } 793 return nil 794 } 795 796 // Check whether the partition name is duplicate 797 func checkPartitionNameUnique(ctx context.Context, pi *plan.PartitionByDef) error { 798 partitions := pi.Partitions 799 partNames := make(map[string]struct{}, len(partitions)) 800 for _, partitionItem := range partitions { 801 if _, ok := partNames[partitionItem.PartitionName]; ok { 802 return moerr.NewSameNamePartition(ctx, partitionItem.PartitionName) 803 } 804 partNames[partitionItem.PartitionName] = struct{}{} 805 } 806 return nil 807 } 808 809 // extractColFromExpr extracts column names from partition expression 810 func extractColFromExpr(nameByColRef map[[2]int32]string, expr *Expr, result map[string]int) { 811 switch exprImpl := expr.Expr.(type) { 812 case *plan.Expr_Col: 813 colRef := nameByColRef[[2]int32{exprImpl.Col.RelPos, exprImpl.Col.ColPos}] 814 split := strings.Split(colRef, ".") 815 colName := split[len(split)-1] 816 result[colName] = 0 817 case *plan.Expr_F: 818 for _, arg := range exprImpl.F.Args { 819 extractColFromExpr(nameByColRef, arg, result) 820 } 821 } 822 } 823 824 func handleEmptyKeyPartition(partitionBinder *PartitionBinder, tableDef *TableDef, partitionDef *plan.PartitionByDef) error { 825 hasValidPrimaryKey := false 826 hasUniqueKey := false 827 var primaryKey *plan.PrimaryKeyDef 828 829 if tableDef.Pkey != nil && !onlyHasHiddenPrimaryKey(tableDef) { 830 hasValidPrimaryKey = true 831 primaryKey = tableDef.Pkey 832 } 833 834 uniqueIndexCount := 0 835 if tableDef.Indexes != nil { 836 for _, indexdef := range tableDef.Indexes { 837 if indexdef.Unique { 838 hasUniqueKey = true 839 uniqueIndexCount++ 840 } 841 } 842 } 843 844 if hasValidPrimaryKey { 845 // Any columns used as the partitioning key must comprise part or all of the table's primary key, if the table has one. 846 // Where no column name is specified as the partitioning key, the table's primary key is used, if there is one. 847 pkcols := make(map[string]int) 848 stringSliceToMap(primaryKey.Names, pkcols) 849 if hasUniqueKey { 850 for _, indexdef := range tableDef.Indexes { 851 if indexdef.Unique { 852 // A UNIQUE INDEX must include all columns in the table's partitioning function 853 uniqueKeys := make(map[string]int) 854 stringSliceToMapForIndexParts(indexdef.Parts, uniqueKeys) 855 if !checkUniqueKeyIncludePartKey(pkcols, uniqueKeys) { 856 return moerr.NewUniqueKeyNeedAllFieldsInPf(partitionBinder.GetContext(), "PRIMARY KEY") 857 } 858 } else { 859 continue 860 } 861 } 862 } 863 } else if hasUniqueKey { 864 // If there is no primary key but there is a unique key, then the unique key is used for the partitioning key 865 if uniqueIndexCount >= 2 { 866 firstUniqueKeyCols := make(map[string]int) 867 for _, indexdef := range tableDef.Indexes { 868 stringSliceToMapForIndexParts(indexdef.Parts, firstUniqueKeyCols) 869 break 870 } 871 872 for _, indexdef := range tableDef.Indexes { 873 uniqueKeys := make(map[string]int) 874 stringSliceToMapForIndexParts(indexdef.Parts, uniqueKeys) 875 if !checkUniqueKeyIncludePartKey(firstUniqueKeyCols, uniqueKeys) { 876 return moerr.NewUniqueKeyNeedAllFieldsInPf(partitionBinder.GetContext(), "PRIMARY KEY") 877 } 878 } 879 } 880 } else { 881 return moerr.NewFieldNotFoundPart(partitionBinder.GetContext()) 882 } 883 return nil 884 }