github.com/matrixorigin/matrixone@v0.7.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 "fmt" 19 "go/constant" 20 "strconv" 21 "strings" 22 23 "github.com/matrixorigin/matrixone/pkg/sql/plan/rule" 24 "github.com/matrixorigin/matrixone/pkg/sql/util" 25 26 "github.com/matrixorigin/matrixone/pkg/common/moerr" 27 "github.com/matrixorigin/matrixone/pkg/container/types" 28 "github.com/matrixorigin/matrixone/pkg/pb/plan" 29 "github.com/matrixorigin/matrixone/pkg/sql/parsers/dialect" 30 "github.com/matrixorigin/matrixone/pkg/sql/parsers/tree" 31 ) 32 33 const ( 34 // Reference link https://dev.mysql.com/doc/mysql-reslimits-excerpt/5.6/en/partitioning-limitations.html 35 PartitionNumberLimit = 8192 36 ) 37 38 // buildHashPartition handle Hash Partitioning 39 func buildHashPartition(partitionBinder *PartitionBinder, stmt *tree.CreateTable, tableDef *TableDef) error { 40 partitionOp := stmt.PartitionOption 41 if partitionOp.SubPartBy != nil { 42 return moerr.NewInvalidInput(partitionBinder.GetContext(), "no subpartition") 43 } 44 partitionsNum := partitionOp.PartBy.Num 45 // If you do not include a PARTITIONS clause, the number of partitions defaults to 1. 46 if partitionsNum <= 0 { 47 partitionsNum = 1 48 } 49 // check partition number 50 if err := checkPartitionsNumber(partitionBinder, partitionsNum); err != nil { 51 return err 52 } 53 54 partitionType := partitionOp.PartBy.PType.(*tree.HashType) 55 partitionInfo := &plan.PartitionByDef{ 56 Partitions: make([]*plan.PartitionItem, partitionsNum), 57 PartitionNum: partitionsNum, 58 IsSubPartition: partitionOp.PartBy.IsSubPartition, 59 } 60 61 if partitionType.Linear { 62 partitionInfo.Type = plan.PartitionType_LINEAR_HASH 63 } else { 64 partitionInfo.Type = plan.PartitionType_HASH 65 } 66 67 planExpr, err := partitionBinder.BindExpr(partitionType.Expr, 0, true) 68 if err != nil { 69 return err 70 } 71 partitionInfo.PartitionExpr = &plan.PartitionExpr{ 72 Expr: planExpr, 73 ExprStr: tree.String(partitionType.Expr, dialect.MYSQL), 74 } 75 76 err = buildPartitionDefinitionsInfo(partitionBinder, partitionInfo, partitionOp.Partitions) 77 if err != nil { 78 return err 79 } 80 err = checkTableDefPartition(partitionBinder, tableDef, partitionInfo) 81 if err != nil { 82 return err 83 } 84 err = buildEvalPartitionExpression(partitionBinder, stmt, partitionInfo) 85 if err != nil { 86 return err 87 } 88 89 partitionInfo.PartitionMsg = tree.String(partitionOp, dialect.MYSQL) 90 tableDef.Partition = partitionInfo 91 return nil 92 } 93 94 // buildKeyPartition handle KEY Partitioning 95 func buildKeyPartition(partitionBinder *PartitionBinder, stmt *tree.CreateTable, tableDef *TableDef) error { 96 partitionOp := stmt.PartitionOption 97 if partitionOp.SubPartBy != nil { 98 return moerr.NewInvalidInput(partitionBinder.GetContext(), "no subpartition") 99 } 100 101 // if you do not include a PARTITIONS clause, the number of partitions defaults to 1. 102 partitionsNum := partitionOp.PartBy.Num 103 if partitionsNum <= 0 { 104 partitionsNum = 1 105 } 106 // check partition number 107 if err := checkPartitionsNumber(partitionBinder, partitionsNum); err != nil { 108 return err 109 } 110 111 partitionType := partitionOp.PartBy.PType.(*tree.KeyType) 112 // check the algorithm option 113 if partitionType.Algorithm != 1 && partitionType.Algorithm != 2 { 114 return moerr.NewInvalidInput(partitionBinder.GetContext(), "the 'ALGORITHM' option has too many values") 115 } 116 117 partitionInfo := &plan.PartitionByDef{ 118 Partitions: make([]*plan.PartitionItem, partitionsNum), 119 PartitionNum: partitionsNum, 120 Algorithm: partitionType.Algorithm, 121 IsSubPartition: partitionOp.PartBy.IsSubPartition, 122 } 123 124 if partitionType.Linear { 125 partitionInfo.Type = plan.PartitionType_LINEAR_KEY 126 } else { 127 partitionInfo.Type = plan.PartitionType_KEY 128 } 129 err := buildPartitionColumns(partitionBinder, partitionInfo, partitionType.ColumnList) 130 if err != nil { 131 return err 132 } 133 134 err = buildPartitionDefinitionsInfo(partitionBinder, partitionInfo, partitionOp.Partitions) 135 if err != nil { 136 return err 137 } 138 err = checkTableDefPartition(partitionBinder, tableDef, partitionInfo) 139 if err != nil { 140 return err 141 } 142 err = buildEvalPartitionExpression(partitionBinder, stmt, partitionInfo) 143 if err != nil { 144 return err 145 } 146 147 partitionInfo.PartitionMsg = tree.String(partitionOp, dialect.MYSQL) 148 tableDef.Partition = partitionInfo 149 return nil 150 } 151 152 // buildRangePartition handle Range Partitioning and Range columns partitioning 153 func buildRangePartition(partitionBinder *PartitionBinder, stmt *tree.CreateTable, tableDef *TableDef) error { 154 partitionOp := stmt.PartitionOption 155 partitionType := partitionOp.PartBy.PType.(*tree.RangeType) 156 157 partitionNum := len(partitionOp.Partitions) 158 if partitionOp.PartBy.Num != 0 && uint64(partitionNum) != partitionOp.PartBy.Num { 159 return moerr.NewParseError(partitionBinder.GetContext(), "build range partition") 160 } 161 162 partitionInfo := &plan.PartitionByDef{ 163 IsSubPartition: partitionOp.PartBy.IsSubPartition, 164 Partitions: make([]*plan.PartitionItem, partitionNum), 165 PartitionNum: uint64(partitionNum), 166 } 167 168 // RANGE Partitioning 169 if len(partitionType.ColumnList) == 0 { 170 partitionInfo.Type = plan.PartitionType_RANGE 171 planExpr, err := partitionBinder.BindExpr(partitionType.Expr, 0, true) 172 if err != nil { 173 return err 174 } 175 partitionInfo.PartitionExpr = &plan.PartitionExpr{ 176 Expr: planExpr, 177 ExprStr: tree.String(partitionType.Expr, dialect.MYSQL), 178 } 179 } else { 180 // RANGE COLUMNS partitioning 181 partitionInfo.Type = plan.PartitionType_RANGE_COLUMNS 182 err := buildPartitionColumns(partitionBinder, partitionInfo, partitionType.ColumnList) 183 if err != nil { 184 return err 185 } 186 } 187 188 err := buildPartitionDefinitionsInfo(partitionBinder, partitionInfo, partitionOp.Partitions) 189 if err != nil { 190 return err 191 } 192 193 err = checkTableDefPartition(partitionBinder, tableDef, partitionInfo) 194 if err != nil { 195 return err 196 } 197 198 err = buildEvalPartitionExpression(partitionBinder, stmt, partitionInfo) 199 if err != nil { 200 return err 201 } 202 203 partitionInfo.PartitionMsg = tree.String(partitionOp, dialect.MYSQL) 204 tableDef.Partition = partitionInfo 205 return nil 206 } 207 208 // buildListPartitiion handle List Partitioning and List columns partitioning 209 func buildListPartitiion(partitionBinder *PartitionBinder, stmt *tree.CreateTable, tableDef *TableDef) error { 210 partitionOp := stmt.PartitionOption 211 partitionType := partitionOp.PartBy.PType.(*tree.ListType) 212 213 partitionNum := len(partitionOp.Partitions) 214 if partitionOp.PartBy.Num != 0 && uint64(partitionNum) != partitionOp.PartBy.Num { 215 return moerr.NewParseError(partitionBinder.GetContext(), "build list partition") 216 } 217 218 partitionInfo := &plan.PartitionByDef{ 219 IsSubPartition: partitionOp.PartBy.IsSubPartition, 220 Partitions: make([]*plan.PartitionItem, partitionNum), 221 PartitionNum: uint64(partitionNum), 222 } 223 224 if len(partitionType.ColumnList) == 0 { 225 partitionInfo.Type = plan.PartitionType_LIST 226 planExpr, err := partitionBinder.BindExpr(partitionType.Expr, 0, true) 227 if err != nil { 228 return err 229 } 230 partitionInfo.PartitionExpr = &plan.PartitionExpr{ 231 Expr: planExpr, 232 ExprStr: tree.String(partitionType.Expr, dialect.MYSQL), 233 } 234 } else { 235 partitionInfo.Type = plan.PartitionType_LIST_COLUMNS 236 err := buildPartitionColumns(partitionBinder, partitionInfo, partitionType.ColumnList) 237 if err != nil { 238 return err 239 } 240 } 241 242 err := buildPartitionDefinitionsInfo(partitionBinder, partitionInfo, partitionOp.Partitions) 243 if err != nil { 244 return err 245 } 246 247 err = checkTableDefPartition(partitionBinder, tableDef, partitionInfo) 248 if err != nil { 249 return err 250 } 251 252 err = buildEvalPartitionExpression(partitionBinder, stmt, partitionInfo) 253 if err != nil { 254 return err 255 } 256 257 partitionInfo.PartitionMsg = tree.String(partitionOp, dialect.MYSQL) 258 tableDef.Partition = partitionInfo 259 return nil 260 } 261 262 // buildPartitionColumns COLUMNS partitioning enables the use of multiple columns in partitioning keys 263 func buildPartitionColumns(partitionBinder *PartitionBinder, partitionInfo *plan.PartitionByDef, columnList []*tree.UnresolvedName) error { 264 columnsExpr := make([]*plan.Expr, len(columnList)) 265 partitionColumns := make([]string, len(columnList)) 266 267 // partition COLUMNS does not accept expressions, only names of columns. 268 for i, column := range columnList { 269 colExpr, err := partitionBinder.BindColRef(column, 0, true) 270 if err != nil { 271 return moerr.NewParseError(partitionBinder.GetContext(), "build partition columns") 272 } 273 columnsExpr[i] = colExpr 274 partitionColumns[i] = tree.String(column, dialect.MYSQL) 275 } 276 // check whether the columns partitioning type is legal 277 if err := checkColumnsPartitionType(partitionBinder, partitionColumns, columnsExpr); err != nil { 278 return err 279 } 280 partitionInfo.PartitionColumns = &plan.PartitionColumns{ 281 Columns: columnsExpr, 282 PartitionColumns: partitionColumns, 283 } 284 return nil 285 } 286 287 func getPrimaryKeyAndUniqueKey(defs tree.TableDefs) (primaryKeys []*tree.UnresolvedName, uniqueIndexs []*tree.UniqueIndex) { 288 for _, item := range defs { 289 switch def := item.(type) { 290 case *tree.ColumnTableDef: 291 for _, attr := range def.Attributes { 292 if _, ok := attr.(*tree.AttributePrimaryKey); ok { 293 primaryKeys = append(primaryKeys, def.Name) 294 } 295 296 if _, ok := attr.(*tree.AttributeUniqueKey); ok { 297 part := &tree.KeyPart{ 298 ColName: def.Name, 299 } 300 uniqueKey := &tree.UniqueIndex{ 301 KeyParts: []*tree.KeyPart{part}, 302 Name: "", 303 Empty: true, 304 } 305 uniqueIndexs = append(uniqueIndexs, uniqueKey) 306 } 307 } 308 case *tree.PrimaryKeyIndex: 309 for _, key := range def.KeyParts { 310 primaryKeys = append(primaryKeys, key.ColName) 311 } 312 case *tree.UniqueIndex: 313 uniqueIndexs = append(uniqueIndexs, def) 314 } 315 } 316 return 317 } 318 319 // This method is used to generate partition ast for key partition and hash partition 320 // For example: abs (hash_value (col3))% 4 321 func genPartitionAst(exprs tree.Exprs, partNum int64) tree.Expr { 322 hashFuncName := tree.SetUnresolvedName(strings.ToLower("hash_value")) 323 hashfuncExpr := &tree.FuncExpr{ 324 Func: tree.FuncName2ResolvableFunctionReference(hashFuncName), 325 Exprs: exprs, 326 } 327 328 absFuncName := tree.SetUnresolvedName(strings.ToLower("abs")) 329 absFuncExpr := &tree.FuncExpr{ 330 Func: tree.FuncName2ResolvableFunctionReference(absFuncName), 331 Exprs: tree.Exprs{hashfuncExpr}, 332 } 333 334 numstr := fmt.Sprintf("%v", partNum) 335 divExpr := tree.NewNumValWithType(constant.MakeInt64(partNum), numstr, false, tree.P_int64) 336 modOpExpr := tree.NewBinaryExpr(tree.MOD, absFuncExpr, divExpr) 337 return modOpExpr 338 } 339 340 // This method is used to convert different types of partition structures into plan.Expr 341 func buildEvalPartitionExpression(partitionBinder *PartitionBinder, stmt *tree.CreateTable, partitionInfo *plan.PartitionByDef) error { 342 partitionOp := stmt.PartitionOption 343 switch partitionType := partitionOp.PartBy.PType.(type) { 344 case *tree.KeyType: 345 // For the Key partition, convert the partition information into the expression,such as : abs (hash_value (expr)) % partitionNum 346 var astExprs []tree.Expr 347 if partitionInfo.PartitionColumns == nil { 348 // Any columns used as the partitioning key must comprise part or all of the table's primary key, if the table has one. 349 // Where no column name is specified as the partitioning key, the table's primary key is used, if there is one. 350 // If there is no primary key but there is a unique key, then the unique key is used for the partitioning key 351 primaryKeys, uniqueIndexs := getPrimaryKeyAndUniqueKey(stmt.Defs) 352 if len(primaryKeys) != 0 { 353 astExprs = make([]tree.Expr, len(primaryKeys)) 354 for i, kexpr := range primaryKeys { 355 astExprs[i] = kexpr 356 } 357 } else if len(uniqueIndexs) != 0 { 358 uniqueKey := uniqueIndexs[0] 359 astExprs = make([]tree.Expr, len(uniqueKey.KeyParts)) 360 for i, keyPart := range uniqueKey.KeyParts { 361 astExprs[i] = keyPart.ColName 362 } 363 } else { 364 return moerr.NewInvalidInput(partitionBinder.GetContext(), "Field in list of fields for partition function not found in table") 365 } 366 } else { 367 keyList := partitionType.ColumnList 368 astExprs = make([]tree.Expr, len(keyList)) 369 for i, expr := range keyList { 370 astExprs[i] = expr 371 } 372 } 373 374 partitionAst := genPartitionAst(astExprs, int64(partitionInfo.PartitionNum)) 375 partitionExpression, err := partitionBinder.baseBindExpr(partitionAst, 0, true) 376 if err != nil { 377 return err 378 } 379 partitionInfo.PartitionExpression = partitionExpression 380 case *tree.HashType: 381 // For the Hash partition, convert the partition information into the expression,such as: abs (hash_value (expr)) % partitionNum 382 hashExpr := partitionType.Expr 383 partitionAst := genPartitionAst(tree.Exprs{hashExpr}, int64(partitionInfo.PartitionNum)) 384 385 partitionExpression, err := partitionBinder.baseBindExpr(partitionAst, 0, true) 386 if err != nil { 387 return err 388 } 389 partitionInfo.PartitionExpression = partitionExpression 390 case *tree.RangeType: 391 // For the Range partition, convert the partition information into the expression,such as: 392 // case when expr < 6 then 0 when expr < 11 then 1 when true then 3 else -1 end 393 if partitionType.ColumnList == nil { 394 rangeExpr := partitionType.Expr 395 partitionExprAst, err := buildRangeCaseWhenExpr(rangeExpr, partitionOp.Partitions) 396 if err != nil { 397 return err 398 } 399 partitionExpression, err := partitionBinder.baseBindExpr(partitionExprAst, 0, true) 400 if err != nil { 401 return err 402 } 403 partitionInfo.PartitionExpression = partitionExpression 404 } else { 405 // For the Range Columns partition, convert the partition information into the expression, such as: 406 // (a, b, c) < (x0, x1, x2) --> a < x0 || (a = x0 && (b < x1 || b = x1 && c < x2)) 407 columnsExpr := partitionType.ColumnList 408 partitionExprAst, err := buildRangeColumnsCaseWhenExpr(columnsExpr, partitionOp.Partitions) 409 if err != nil { 410 return err 411 } 412 partitionExpression, err := partitionBinder.baseBindExpr(partitionExprAst, 0, true) 413 if err != nil { 414 return err 415 } 416 partitionInfo.PartitionExpression = partitionExpression 417 } 418 419 case *tree.ListType: 420 // For the List partition, convert the partition information into the expression, such as: 421 // case when expr in (1, 5, 9, 13, 17) then 0 when expr in (2, 6, 10, 14, 18) then 1 else -1 end 422 if partitionType.ColumnList == nil { 423 listExpr := partitionType.Expr 424 partitionExprAst, err := buildListCaseWhenExpr(listExpr, partitionOp.Partitions) 425 if err != nil { 426 return err 427 } 428 partitionExpression, err := partitionBinder.baseBindExpr(partitionExprAst, 0, true) 429 if err != nil { 430 return err 431 } 432 partitionInfo.PartitionExpression = partitionExpression 433 } else { 434 // For the List Columns partition, convert the partition information into the expression, such as: 435 // case when col1 = 0 and col2 = 0 or col1 = null and col2 = null then 0 436 // when col1 = 0 and col2 = 1 or col1 = 0 and col2 = 2 or col1 = 0 and col2 = 3 then 1 437 // when col1 = 1 and col2 = 0 or col1 = 2 and col2 = 0 or col1 = 2 and col2 = 1 then 2 438 // else -1 end 439 columnsExpr := partitionType.ColumnList 440 partitionExprAst, err := buildListColumnsCaseWhenExpr(columnsExpr, partitionOp.Partitions) 441 if err != nil { 442 return err 443 } 444 partitionExpression, err := partitionBinder.baseBindExpr(partitionExprAst, 0, true) 445 if err != nil { 446 return err 447 } 448 partitionInfo.PartitionExpression = partitionExpression 449 } 450 } 451 return nil 452 } 453 454 // This method is used to convert the list columns partition into case when expression,such as: 455 // PARTITION BY LIST COLUMNS(a,b) ( 456 // PARTITION p0 VALUES IN( (0,0), (NULL,NULL) ), 457 // PARTITION p1 VALUES IN( (0,1), (0,2) ), 458 // PARTITION p2 VALUES IN( (1,0), (2,0) ) 459 // );--> 460 // case 461 // when a = 0 and b = 0 or a = null and b = null then 0 462 // when a = 0 and b = 1 or a = 0 and b = 2 then 1 463 // when a = 1 and b = 0 or a = 2 and b = 0 then 2 464 // else -1 465 // end 466 func buildListColumnsCaseWhenExpr(columnsExpr []*tree.UnresolvedName, defs []*tree.Partition) (*tree.CaseExpr, error) { 467 whens := make([]*tree.When, len(defs)) 468 469 for i, partition := range defs { 470 valuesIn := partition.Values.(*tree.ValuesIn) 471 472 elements := make([]tree.Expr, len(valuesIn.ValueList)) 473 for j, value := range valuesIn.ValueList { 474 if tuple, ok := value.(*tree.Tuple); ok { 475 exprs := tuple.Exprs 476 if len(exprs) != len(columnsExpr) { 477 panic("the number of IN expression parameters does not match") 478 } 479 480 if len(columnsExpr) == 1 { 481 newExpr := tree.NewComparisonExpr(tree.EQUAL, columnsExpr[0], exprs[0]) 482 elements[j] = newExpr 483 continue 484 } 485 486 if len(columnsExpr) >= 2 { 487 var andExpr tree.Expr 488 489 first := true 490 for k, lexpr := range columnsExpr { 491 if first { 492 andExpr = tree.NewComparisonExpr(tree.EQUAL, lexpr, exprs[k]) 493 first = false 494 continue 495 } 496 newExpr := tree.NewComparisonExpr(tree.EQUAL, lexpr, exprs[k]) 497 andExpr = tree.NewAndExpr(andExpr, newExpr) 498 } 499 elements[j] = andExpr 500 continue 501 } 502 } else { 503 if len(columnsExpr) != 1 { 504 panic("the number of IN expression parameters does not match") 505 } 506 newExpr := tree.NewComparisonExpr(tree.EQUAL, columnsExpr[0], value) 507 elements[j] = newExpr 508 continue 509 } 510 } 511 512 var conditionExpr tree.Expr 513 if len(valuesIn.ValueList) == 1 { 514 conditionExpr = elements[0] 515 } 516 517 if len(valuesIn.ValueList) > 1 { 518 for m := 1; m < len(elements); m++ { 519 if m == 1 { 520 conditionExpr = tree.NewOrExpr(elements[m-1], elements[m]) 521 } else { 522 conditionExpr = tree.NewOrExpr(conditionExpr, elements[m]) 523 } 524 } 525 } 526 527 when := &tree.When{ 528 Cond: conditionExpr, 529 Val: tree.NewNumValWithType(constant.MakeInt64(int64(i)), fmt.Sprintf("%v", i), false, tree.P_int64), 530 } 531 whens[i] = when 532 } 533 caseWhenExpr := &tree.CaseExpr{ 534 Expr: nil, 535 Whens: whens, 536 Else: tree.NewNumValWithType(constant.MakeInt64(int64(-1)), fmt.Sprintf("%v", -1), false, tree.P_int64), 537 } 538 return caseWhenExpr, nil 539 } 540 541 // This method is used to convert the range partition into case when expression,such as: 542 // PARTITION BY RANGE (code + 5) ( 543 // PARTITION p0 VALUES LESS THAN (6), 544 // PARTITION p1 VALUES LESS THAN (11), 545 // PARTITION p2 VALUES LESS THAN (MAXVALUE), 546 // ); --> 547 // case when (code + 5) < 6 then 0 when (code + 5) < 11 then 1 when true then 3 else -1 end 548 func buildRangeCaseWhenExpr(pexpr tree.Expr, defs []*tree.Partition) (*tree.CaseExpr, error) { 549 whens := make([]*tree.When, len(defs)) 550 for i, partition := range defs { 551 valuesLessThan := partition.Values.(*tree.ValuesLessThan) 552 if len(valuesLessThan.ValueList) != 1 { 553 panic("range partition less than expression should have one element") 554 } 555 valueExpr := valuesLessThan.ValueList[0] 556 557 var conditionExpr tree.Expr 558 if _, ok := valueExpr.(*tree.MaxValue); ok { 559 conditionExpr = tree.NewNumValWithType(constant.MakeBool(true), "true", false, tree.P_bool) 560 } else { 561 LessThanExpr := tree.NewComparisonExpr(tree.LESS_THAN, pexpr, valueExpr) 562 conditionExpr = LessThanExpr 563 } 564 565 when := &tree.When{ 566 Cond: conditionExpr, 567 Val: tree.NewNumValWithType(constant.MakeInt64(int64(i)), fmt.Sprintf("%v", i), false, tree.P_int64), 568 } 569 whens[i] = when 570 } 571 572 caseWhenExpr := &tree.CaseExpr{ 573 Expr: nil, 574 Whens: whens, 575 Else: tree.NewNumValWithType(constant.MakeInt64(int64(-1)), fmt.Sprintf("%v", -1), false, tree.P_int64), 576 } 577 return caseWhenExpr, nil 578 } 579 580 // This method is used to optimize the row constructor expression in range columns partition item into a common logical operation expression, 581 // such as: (a, b, c) < (x0, x1, x2) -> a < x0 || (a = x0 && (b < x1 || b = x1 && c < x2)) 582 func buildRangeColumnsCaseWhenExpr(columnsExpr []*tree.UnresolvedName, defs []*tree.Partition) (*tree.CaseExpr, error) { 583 whens := make([]*tree.When, len(defs)) 584 for i, partition := range defs { 585 valuesLessThan := partition.Values.(*tree.ValuesLessThan) 586 587 if len(valuesLessThan.ValueList) != len(columnsExpr) { 588 panic("the number of less value expression parameters does not match") 589 } 590 591 var tempExpr tree.Expr 592 for j := len(valuesLessThan.ValueList) - 1; j >= 0; j-- { 593 valueExpr := valuesLessThan.ValueList[j] 594 if j == len(valuesLessThan.ValueList)-1 { 595 if _, ok := valueExpr.(*tree.MaxValue); ok { 596 trueExpr := tree.NewNumValWithType(constant.MakeBool(true), "true", false, tree.P_bool) 597 tempExpr = trueExpr 598 } else { 599 lessThanExpr := tree.NewComparisonExpr(tree.LESS_THAN, columnsExpr[j], valueExpr) 600 tempExpr = lessThanExpr 601 } 602 continue 603 } else { 604 var firstExpr tree.Expr 605 if _, ok := valueExpr.(*tree.MaxValue); ok { 606 trueExpr := tree.NewNumValWithType(constant.MakeBool(true), "true", false, tree.P_bool) 607 firstExpr = trueExpr 608 } else { 609 lessThanExpr := tree.NewComparisonExpr(tree.LESS_THAN, columnsExpr[j], valueExpr) 610 firstExpr = lessThanExpr 611 } 612 613 var middleExpr tree.Expr 614 if _, ok := valueExpr.(*tree.MaxValue); ok { 615 trueExpr := tree.NewNumValWithType(constant.MakeBool(true), "true", false, tree.P_bool) 616 middleExpr = trueExpr 617 } else { 618 equalExpr := tree.NewComparisonExpr(tree.EQUAL, columnsExpr[j], valueExpr) 619 middleExpr = equalExpr 620 } 621 secondExpr := tree.NewAndExpr(middleExpr, tempExpr) 622 tempExpr = tree.NewOrExpr(firstExpr, secondExpr) 623 } 624 } 625 626 when := &tree.When{ 627 Cond: tempExpr, 628 Val: tree.NewNumValWithType(constant.MakeInt64(int64(i)), fmt.Sprintf("%v", i), false, tree.P_int64), 629 } 630 whens[i] = when 631 } 632 caseWhenExpr := &tree.CaseExpr{ 633 Expr: nil, 634 Whens: whens, 635 Else: tree.NewNumValWithType(constant.MakeInt64(int64(-1)), fmt.Sprintf("%v", -1), false, tree.P_int64), 636 } 637 return caseWhenExpr, nil 638 } 639 640 // This method is used to convert the list columns partition into an case when expression,such as: 641 // PARTITION BY LIST (expr) ( 642 // PARTITION p0 VALUES IN(1, 5, 9, 13, 17), 643 // PARTITION p1 VALUES IN (2, 6, 10, 14, 18) 644 // );--> 645 // case when expr in (1, 5, 9, 13, 17) then 0 when expr in (2, 6, 10, 14, 18) then 1 else -1 end 646 func buildListCaseWhenExpr(listExpr tree.Expr, defs []*tree.Partition) (*tree.CaseExpr, error) { 647 whens := make([]*tree.When, len(defs)) 648 for i, partition := range defs { 649 valuesIn := partition.Values.(*tree.ValuesIn) 650 651 tuple := tree.NewTuple(valuesIn.ValueList) 652 inExpr := tree.NewComparisonExpr(tree.IN, listExpr, tuple) 653 654 when := &tree.When{ 655 Cond: inExpr, 656 Val: tree.NewNumValWithType(constant.MakeInt64(int64(i)), fmt.Sprintf("%v", i), false, tree.P_int64), 657 } 658 whens[i] = when 659 } 660 caseWhenExpr := &tree.CaseExpr{ 661 Expr: nil, 662 Whens: whens, 663 Else: tree.NewNumValWithType(constant.MakeInt64(int64(-1)), fmt.Sprintf("%v", -1), false, tree.P_int64), 664 } 665 return caseWhenExpr, nil 666 } 667 668 // buildPartitionDefinitionsInfo build partition definitions info without assign partition id. 669 func buildPartitionDefinitionsInfo(partitionBinder *PartitionBinder, partitionInfo *plan.PartitionByDef, defs []*tree.Partition) (err error) { 670 switch partitionInfo.Type { 671 case plan.PartitionType_HASH, plan.PartitionType_LINEAR_HASH: 672 err = buildHashPartitionDefinitions(partitionBinder, defs, partitionInfo) 673 case plan.PartitionType_KEY, plan.PartitionType_LINEAR_KEY: 674 err = buildKeyPartitionDefinitions(partitionBinder, defs, partitionInfo) 675 case plan.PartitionType_RANGE, plan.PartitionType_RANGE_COLUMNS: 676 err = buildRangePartitionDefinitions(partitionBinder, defs, partitionInfo) 677 case plan.PartitionType_LIST, plan.PartitionType_LIST_COLUMNS: 678 err = buildListPartitionDefinitions(partitionBinder, defs, partitionInfo) 679 } 680 return err 681 } 682 683 func buildRangePartitionDefinitions(partitionBinder *PartitionBinder, defs []*tree.Partition, partitionInfo *plan.PartitionByDef) error { 684 // VALUES LESS THAN value must be strictly increasing for each partition 685 for i, partition := range defs { 686 partitionItem := &plan.PartitionItem{ 687 PartitionName: string(partition.Name), 688 OrdinalPosition: uint32(i + 1), 689 } 690 691 if valuesLessThan, ok := partition.Values.(*tree.ValuesLessThan); ok { 692 planExprs := make([]*plan.Expr, len(valuesLessThan.ValueList)) 693 binder := NewPartitionBinder(nil, nil) 694 695 for j, valueExpr := range valuesLessThan.ValueList { 696 // value must be able to evaluate the expression's return value 697 planExpr, err := binder.BindExpr(valueExpr, 0, false) 698 if err != nil { 699 return err 700 } 701 planExprs[j] = planExpr 702 } 703 704 partitionItem.LessThan = planExprs 705 partitionItem.Description = tree.String(valuesLessThan.ValueList, dialect.MYSQL) 706 } else { 707 return moerr.NewInternalError(partitionBinder.GetContext(), "RANGE PARTITIONING can only use VALUES LESS THAN definition") 708 } 709 710 for _, tableOption := range partition.Options { 711 if opComment, ok := tableOption.(*tree.TableOptionComment); ok { 712 partitionItem.Comment = opComment.Comment 713 } 714 } 715 partitionInfo.Partitions[i] = partitionItem 716 } 717 return buildRangePartitionItem(partitionBinder, partitionInfo, defs) 718 } 719 720 func buildListPartitionDefinitions(partitionBinder *PartitionBinder, defs []*tree.Partition, partitionInfo *plan.PartitionByDef) error { 721 for i, partition := range defs { 722 partitionItem := &plan.PartitionItem{ 723 PartitionName: string(partition.Name), 724 OrdinalPosition: uint32(i + 1), 725 } 726 727 if valuesIn, ok := partition.Values.(*tree.ValuesIn); ok { 728 binder := NewPartitionBinder(nil, nil) 729 inValues := make([]*plan.Expr, len(valuesIn.ValueList)) 730 731 for j, value := range valuesIn.ValueList { 732 tuple, err := binder.BindExpr(value, 0, false) 733 if err != nil { 734 return err 735 } 736 inValues[j] = tuple 737 } 738 partitionItem.InValues = inValues 739 partitionItem.Description = tree.String(valuesIn, dialect.MYSQL) 740 } else { 741 return moerr.NewInternalError(partitionBinder.GetContext(), "LIST PARTITIONING can only use VALUES IN definition") 742 } 743 partitionInfo.Partitions[i] = partitionItem 744 } 745 return buildListPartitionItem(partitionBinder, partitionInfo, defs) 746 } 747 748 func buildHashPartitionDefinitions(partitionBinder *PartitionBinder, defs []*tree.Partition, partitionInfo *plan.PartitionByDef) error { 749 for i := uint64(0); i < partitionInfo.PartitionNum; i++ { 750 partition := &plan.PartitionItem{ 751 PartitionName: "p" + strconv.FormatUint(i, 10), 752 OrdinalPosition: uint32(i + 1), 753 } 754 partitionInfo.Partitions[i] = partition 755 } 756 return nil 757 } 758 759 func buildKeyPartitionDefinitions(partitionBinder *PartitionBinder, defs []*tree.Partition, partitionInfo *plan.PartitionByDef) error { 760 for i := uint64(0); i < partitionInfo.PartitionNum; i++ { 761 partition := &plan.PartitionItem{ 762 PartitionName: "p" + strconv.FormatUint(i, 10), 763 OrdinalPosition: uint32(i + 1), 764 } 765 partitionInfo.Partitions[i] = partition 766 } 767 return nil 768 } 769 770 // The permitted data types are shown in the following list: 771 // All integer types 772 // DATE and DATETIME 773 // CHAR, VARCHAR, BINARY, and VARBINARY 774 // See https://dev.mysql.com/doc/mysql-partitioning-excerpt/5.7/en/partitioning-columns.html 775 func checkColumnsPartitionType(partitionBinder *PartitionBinder, columnNames []string, columnPlanExprs []*plan.Expr) error { 776 for i, planexpr := range columnPlanExprs { 777 t := types.T(planexpr.Typ.Id) 778 if !types.IsInteger(t) && !types.IsString(t) && !types.IsDateRelate(t) { 779 return moerr.NewSyntaxError(partitionBinder.GetContext(), "type %s of column %s not allowd in partition clause", t.String(), columnNames[i]) 780 } 781 } 782 return nil 783 } 784 785 // checkTableDefPartition Perform integrity constraint check on partitions of create table statement 786 func checkTableDefPartition(partitionBinder *PartitionBinder, tableDef *TableDef, partitionInfo *plan.PartitionByDef) error { 787 if err := checkPartitionFuncType(partitionBinder, tableDef, partitionInfo); err != nil { 788 return err 789 } 790 if err := checkPartitionDefinitionConstraints(partitionBinder, partitionInfo, tableDef); err != nil { 791 return err 792 } 793 if err := checkPartitionKeysConstraints(partitionBinder, tableDef, partitionInfo); err != nil { 794 return err 795 } 796 if partitionInfo.Type == plan.PartitionType_KEY || partitionInfo.Type == plan.PartitionType_LINEAR_KEY { 797 //if len(partitionInfo.Columns) == 0 { 798 if len(partitionInfo.PartitionColumns.Columns) == 0 { 799 return handleEmptyKeyPartition(partitionBinder, tableDef, partitionInfo) 800 } 801 } 802 return nil 803 } 804 805 // check partition expression type 806 func checkPartitionFuncType(partitionBinder *PartitionBinder, tableDef *TableDef, partitionInfo *plan.PartitionByDef) error { 807 //if partitionInfo.Expr == nil { 808 if partitionInfo.PartitionExpr == nil { 809 return nil 810 } else { 811 //expr := partitionInfo.Expr 812 expr := partitionInfo.PartitionExpr.Expr 813 // expr must return a nonconstant, nonrandom integer value 814 if rule.IsConstant(expr) { 815 return moerr.NewInvalidInput(partitionBinder.GetContext(), "partition functin is not const") 816 } 817 818 t := types.T(expr.Typ.Id) 819 if !types.IsInteger(t) { 820 return moerr.NewSyntaxError(partitionBinder.GetContext(), "type %s not allowed in partition clause", t.String()) 821 } 822 } 823 return nil 824 } 825 826 // checkPartitionKeysConstraints checks the partitioning key is included in the table constraint. 827 func checkPartitionKeysConstraints(partitionBinder *PartitionBinder, tableDef *TableDef, partitionInfo *plan.PartitionByDef) error { 828 hasPrimaryKey := false 829 var primaryKey *plan.PrimaryKeyDef 830 if tableDef.Pkey != nil { 831 hasPrimaryKey = true 832 primaryKey = tableDef.Pkey 833 } 834 835 hasUniqueKey := false 836 if tableDef.Indexes != nil { 837 for _, indexdef := range tableDef.Indexes { 838 if indexdef.Unique { 839 hasUniqueKey = true 840 break 841 } 842 } 843 } 844 845 if hasPrimaryKey { 846 var pkcols []string 847 if len(primaryKey.Names) > 0 && util.JudgeIsCompositePrimaryKeyColumn(primaryKey.Names[0]) { 848 pkcols = util.SplitCompositePrimaryKeyColumnName(primaryKey.Names[0]) 849 } else { 850 pkcols = primaryKey.Names 851 } 852 853 if partitionInfo.PartitionColumns != nil { 854 if !checkUniqueKeyIncludePartKey(partitionInfo.PartitionColumns.PartitionColumns, pkcols) { 855 return moerr.NewInvalidInput(partitionBinder.GetContext(), "partition key is not part of primary key") 856 } 857 } else { 858 extractCols := extractColFromExpr(partitionBinder, partitionInfo.PartitionExpr.Expr) 859 if !checkUniqueKeyIncludePartKey(extractCols, pkcols) { 860 return moerr.NewInvalidInput(partitionBinder.GetContext(), "partition key is not part of primary key") 861 } 862 } 863 } 864 865 if hasUniqueKey { 866 for _, indexdef := range tableDef.Indexes { 867 if indexdef.Unique { 868 uniqueKeyCols := indexdef.Parts 869 if partitionInfo.PartitionColumns != nil { 870 if !checkUniqueKeyIncludePartKey(partitionInfo.PartitionColumns.PartitionColumns, uniqueKeyCols) { 871 return moerr.NewInvalidInput(partitionBinder.GetContext(), "partition key is not part of primary key") 872 } 873 } else { 874 extractCols := extractColFromExpr(partitionBinder, partitionInfo.PartitionExpr.Expr) 875 if !checkUniqueKeyIncludePartKey(extractCols, uniqueKeyCols) { 876 return moerr.NewInvalidInput(partitionBinder.GetContext(), "partition key is not part of primary key") 877 } 878 } 879 } else { 880 continue 881 } 882 } 883 } 884 885 if partitionInfo.Type == plan.PartitionType_KEY { 886 if len(partitionInfo.PartitionColumns.Columns) == 0 && !hasUniqueKey && !hasPrimaryKey { 887 return moerr.NewInvalidInput(partitionBinder.GetContext(), "Field in list of fields for partition function not found in table") 888 } 889 } 890 891 return nil 892 } 893 894 // checkUniqueKeyIncludePartKey checks the partitioning key is included in the constraint(primary key and unique key). 895 func checkUniqueKeyIncludePartKey(partitionKeys []string, uqkeys []string) bool { 896 for i := 0; i < len(partitionKeys); i++ { 897 partitionKey := partitionKeys[i] 898 if !findColumnInIndexCols(partitionKey, uqkeys) { 899 return false 900 } 901 } 902 return true 903 } 904 905 func findColumnInIndexCols(c string, pkcols []string) bool { 906 for _, c1 := range pkcols { 907 if strings.EqualFold(c, c1) { 908 return true 909 } 910 } 911 return false 912 } 913 914 func checkPartitionDefinitionConstraints(partitionBinder *PartitionBinder, partitionInfo *plan.PartitionByDef, tableDef *TableDef) error { 915 var err error 916 if err = checkPartitionNameUnique(partitionBinder, partitionInfo); err != nil { 917 return err 918 } 919 if err = checkPartitionsNumber(partitionBinder, uint64(len(partitionInfo.Partitions))); err != nil { 920 return err 921 } 922 if err = checkPartitionColumnsUnique(partitionBinder, partitionInfo); err != nil { 923 return err 924 } 925 926 if len(partitionInfo.Partitions) == 0 { 927 if partitionInfo.Type == plan.PartitionType_RANGE || partitionInfo.Type == plan.PartitionType_RANGE_COLUMNS { 928 return moerr.NewInvalidInput(partitionBinder.GetContext(), "range partition cannot be empty") 929 } else if partitionInfo.Type == plan.PartitionType_LIST || partitionInfo.Type == plan.PartitionType_LIST_COLUMNS { 930 return moerr.NewInvalidInput(partitionBinder.GetContext(), "list partition cannot be empty") 931 } 932 } 933 934 switch partitionInfo.Type { 935 case plan.PartitionType_RANGE: 936 // TODO 937 case plan.PartitionType_HASH: 938 // TODO 939 case plan.PartitionType_LIST: 940 err = checkPartitionByList(partitionBinder, partitionInfo, tableDef) 941 } 942 return err 943 } 944 945 // checkPartitionColumnsUnique check partition columns for duplicate columns 946 func checkPartitionColumnsUnique(partitionBinder *PartitionBinder, partitionInfo *plan.PartitionByDef) error { 947 if partitionInfo.PartitionColumns == nil || len(partitionInfo.PartitionColumns.PartitionColumns) <= 1 { 948 return nil 949 } 950 var columnsMap = make(map[string]byte) 951 for _, column := range partitionInfo.PartitionColumns.PartitionColumns { 952 if _, ok := columnsMap[column]; ok { 953 return moerr.NewSyntaxError(partitionBinder.GetContext(), "duplicate partition column %s", column) 954 } 955 columnsMap[column] = 1 956 } 957 return nil 958 } 959 960 // checkPartitionsNumber: check whether check partition number exceeds the limit 961 func checkPartitionsNumber(partitionBinder *PartitionBinder, partNum uint64) error { 962 if partNum > uint64(PartitionNumberLimit) { 963 return moerr.NewInvalidInput(partitionBinder.GetContext(), "too many (%d) partitions", partNum) 964 } 965 return nil 966 } 967 968 // Check whether the partition name is duplicate 969 func checkPartitionNameUnique(partitionBinder *PartitionBinder, pd *plan.PartitionByDef) error { 970 partitions := pd.Partitions 971 972 partNames := make(map[string]byte, len(partitions)) 973 for _, par := range partitions { 974 if _, ok := partNames[par.PartitionName]; ok { 975 return moerr.NewSyntaxError(partitionBinder.GetContext(), "duplicate partition name %s", par.PartitionName) 976 } 977 partNames[par.PartitionName] = 1 978 } 979 return nil 980 } 981 982 // extractColFromExpr: extract column names from partition expression 983 func extractColFromExpr(partitionBinder *PartitionBinder, expr *Expr) []string { 984 result := make([]string, 0) 985 switch exprImpl := expr.Expr.(type) { 986 case *plan.Expr_Col: 987 builder := partitionBinder.builder 988 tableColName := builder.nameByColRef[[2]int32{exprImpl.Col.RelPos, exprImpl.Col.ColPos}] 989 split := strings.Split(tableColName, ".") 990 colName := split[len(split)-1] 991 result = append(result, colName) 992 case *plan.Expr_F: 993 tmpcols := extractColFromFunc(partitionBinder, exprImpl) 994 result = append(result, tmpcols...) 995 } 996 return result 997 } 998 999 // extractColFromFunc extract column names from function expression 1000 func extractColFromFunc(partitionBinder *PartitionBinder, funcExpr *plan.Expr_F) []string { 1001 result := make([]string, 0) 1002 for _, arg := range funcExpr.F.Args { 1003 tmpcols := extractColFromExpr(partitionBinder, arg) 1004 result = append(result, tmpcols...) 1005 } 1006 return result 1007 } 1008 1009 func handleEmptyKeyPartition(partitionBinder *PartitionBinder, tableDef *TableDef, partitionInfo *plan.PartitionByDef) error { 1010 hasPrimaryKey := false 1011 hasUniqueKey := false 1012 var primaryKey *plan.PrimaryKeyDef 1013 1014 if tableDef.Pkey != nil { 1015 hasPrimaryKey = true 1016 primaryKey = tableDef.Pkey 1017 } 1018 1019 uniqueIndexCount := 0 1020 if tableDef.Indexes != nil { 1021 for _, indexdef := range tableDef.Indexes { 1022 if indexdef.Unique { 1023 hasUniqueKey = true 1024 uniqueIndexCount++ 1025 } 1026 } 1027 } 1028 1029 if hasPrimaryKey { 1030 // Any columns used as the partitioning key must comprise part or all of the table's primary key, if the table has one. 1031 // Where no column name is specified as the partitioning key, the table's primary key is used, if there is one. 1032 pkcols := primaryKey.Names 1033 1034 if hasUniqueKey { 1035 for _, indexdef := range tableDef.Indexes { 1036 if indexdef.Unique { 1037 // A UNIQUE INDEX must include all columns in the table's partitioning function 1038 if !checkUniqueKeyIncludePartKey(pkcols, indexdef.Parts) { 1039 return moerr.NewInvalidInput(partitionBinder.GetContext(), "partition key is not part of primary key") 1040 } 1041 } else { 1042 continue 1043 } 1044 } 1045 } 1046 } else if hasUniqueKey { 1047 // If there is no primary key but there is a unique key, then the unique key is used for the partitioning key 1048 if uniqueIndexCount >= 2 { 1049 var firstUniqueKeyCols []string 1050 for _, indexdef := range tableDef.Indexes { 1051 firstUniqueKeyCols = indexdef.Parts 1052 break 1053 } 1054 for _, indexdef := range tableDef.Indexes { 1055 if !checkUniqueKeyIncludePartKey(firstUniqueKeyCols, indexdef.Parts) { 1056 return moerr.NewInvalidInput(partitionBinder.GetContext(), "partition key is not part of primary key") 1057 } 1058 } 1059 } 1060 } else { 1061 return moerr.NewInvalidInput(partitionBinder.GetContext(), "Field in list of fields for partition function not found in table") 1062 } 1063 return nil 1064 }