github.com/matrixorigin/matrixone@v0.7.0/pkg/sql/plan/partition_util.go (about) 1 // Copyright 2021 - 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 "github.com/matrixorigin/matrixone/pkg/common/moerr" 20 "github.com/matrixorigin/matrixone/pkg/container/batch" 21 "github.com/matrixorigin/matrixone/pkg/container/types" 22 "github.com/matrixorigin/matrixone/pkg/pb/plan" 23 "github.com/matrixorigin/matrixone/pkg/sql/parsers/tree" 24 "github.com/matrixorigin/matrixone/pkg/vm/process" 25 ) 26 27 // add this code in buildListPartitionItem 28 // return buildListPartitionItem(partitionBinder, partitionInfo, defs) 29 func buildListPartitionItem(binder *PartitionBinder, partitionInfo *plan.PartitionByDef, defs []*tree.Partition) error { 30 for _, def := range defs { 31 if partitionInfo.PartitionColumns != nil { 32 if err := checkListColumnsTypeAndValuesMatch(binder, partitionInfo, def); err != nil { 33 return err 34 } 35 } else { 36 if err := checkListPartitionValuesIsInt(binder, def, partitionInfo); err != nil { 37 return err 38 } 39 } 40 } 41 return nil 42 } 43 44 func checkListColumnsTypeAndValuesMatch(binder *PartitionBinder, partitionInfo *plan.PartitionByDef, partition *tree.Partition) error { 45 if valuesIn, ok := partition.Values.(*tree.ValuesIn); ok { 46 exprs := valuesIn.ValueList 47 48 // Validate() has already checked len(colNames) = len(exprs) 49 // create table ... partition by range columns (cols) 50 // partition p0 values less than (expr) 51 // check the type of cols[i] and expr is consistent. 52 colTypes := collectColumnsType(partitionInfo) 53 for _, colExpr := range exprs { 54 val, err := binder.BindExpr(colExpr, 0, true) 55 if err != nil { 56 return err 57 } 58 59 switch tuple := val.Expr.(type) { 60 case *plan.Expr_List: 61 if len(colTypes) != len(tuple.List.List) { 62 return moerr.NewInternalError(binder.GetContext(), "Inconsistency in usage of column lists for partitioning") 63 } 64 for i, elem := range tuple.List.List { 65 switch elem.Expr.(type) { 66 case *plan.Expr_C: 67 case *plan.Expr_F: 68 default: 69 return moerr.NewInternalError(binder.GetContext(), "This partition function is not allowed") 70 } 71 72 colType := colTypes[i] 73 // Check val.ConvertTo(colType) doesn't work, so we need this case by case check. 74 err = partitionValueTypeCheck(binder.GetContext(), colType, elem.Typ) 75 if err != nil { 76 return err 77 } 78 } 79 case *plan.Expr_C, *plan.Expr_F: 80 if len(colTypes) != 1 { 81 return moerr.NewInternalError(binder.GetContext(), "Inconsistency in usage of column lists for partitioning") 82 } else { 83 err = partitionValueTypeCheck(binder.GetContext(), colTypes[0], val.Typ) 84 if err != nil { 85 return err 86 } 87 } 88 default: 89 return moerr.NewInternalError(binder.GetContext(), "This partition function is not allowed") 90 } 91 } 92 return nil 93 } else { 94 return moerr.NewInternalError(binder.GetContext(), "list partition function is not values in expression") 95 } 96 } 97 98 // check whether the types of partition functions and partition values match 99 func partitionValueTypeCheck(ctx context.Context, funcTyp *Type, valueTyp *Type) error { 100 switch types.T(funcTyp.Id) { 101 case types.T_date, types.T_datetime: 102 switch types.T(valueTyp.Id) { 103 case types.T_varchar, types.T_char: 104 default: 105 return moerr.NewInternalError(ctx, "Partition column values of incorrect type") 106 } 107 case types.T_int8, types.T_int16, types.T_int32, types.T_int64, types.T_uint8, types.T_uint16, types.T_uint32, types.T_uint64: 108 switch types.T(valueTyp.Id) { 109 case types.T_int8, types.T_int16, types.T_int32, types.T_int64, types.T_uint8, types.T_uint16, types.T_uint32, types.T_uint64, types.T_any: 110 default: 111 return moerr.NewInternalError(ctx, "Partition column values of incorrect type") 112 } 113 case types.T_float32, types.T_float64: 114 switch types.T(valueTyp.Id) { 115 case types.T_float32, types.T_float64, types.T_any: 116 default: 117 return moerr.NewInternalError(ctx, "Partition column values of incorrect type") 118 } 119 case types.T_varchar, types.T_char: 120 switch types.T(valueTyp.Id) { 121 case types.T_varchar, types.T_char, types.T_any: 122 default: 123 return moerr.NewInternalError(ctx, "Partition column values of incorrect type") 124 } 125 } 126 return nil 127 } 128 129 func checkListPartitionValuesIsInt(binder *PartitionBinder, partition *tree.Partition, info *plan.PartitionByDef) error { 130 unsignedFlag := types.IsUnsignedInt(types.T(info.PartitionExpr.Expr.Typ.Id)) 131 if valuesIn, ok := partition.Values.(*tree.ValuesIn); ok { 132 exprs := valuesIn.ValueList 133 for _, exp := range exprs { 134 if _, ok := exp.(*tree.MaxValue); ok { 135 continue 136 } 137 val, err := binder.BindExpr(exp, 0, true) 138 if err != nil { 139 return err 140 } 141 142 compilerContext := binder.builder.compCtx 143 evalExpr, err := EvalPlanExpr(binder.GetContext(), val, compilerContext.GetProcess()) 144 if err != nil { 145 return err 146 } 147 148 cval, ok1 := evalExpr.Expr.(*plan.Expr_C) 149 if !ok1 { 150 return moerr.NewInternalError(binder.GetContext(), "This partition function is not allowed") 151 } 152 153 switch types.T(evalExpr.Typ.Id) { 154 case types.T_uint8, types.T_uint16, types.T_uint32, types.T_uint64, types.T_any: 155 case types.T_int8, types.T_int16, types.T_int32, types.T_int64: 156 switch value := cval.C.Value.(type) { 157 case *plan.Const_I8Val: 158 if value.I8Val < 0 && unsignedFlag { 159 return moerr.NewInternalError(binder.GetContext(), "Partition constant is out of partition function domain") 160 } 161 case *plan.Const_I16Val: 162 if value.I16Val < 0 && unsignedFlag { 163 return moerr.NewInternalError(binder.GetContext(), "Partition constant is out of partition function domain") 164 } 165 case *plan.Const_I32Val: 166 if value.I32Val < 0 && unsignedFlag { 167 return moerr.NewInternalError(binder.GetContext(), "Partition constant is out of partition function domain") 168 } 169 case *plan.Const_I64Val: 170 if value.I64Val < 0 && unsignedFlag { 171 return moerr.NewInternalError(binder.GetContext(), "Partition constant is out of partition function domain") 172 } 173 default: 174 return moerr.NewInternalError(binder.GetContext(), "VALUES value for partition '%-.64s' must have type INT", partition.Name) 175 } 176 default: 177 return moerr.NewInternalError(binder.GetContext(), "VALUES value for partition '%-.64s' must have type INT", partition.Name) 178 } 179 } 180 } 181 return nil 182 } 183 184 // add this code in buildRangePartitionDefinitions 185 // return buildRangePartitionDefinitionItem(partitionBinder, partitionInfo, defs) 186 func buildRangePartitionItem(binder *PartitionBinder, partitionInfo *plan.PartitionByDef, defs []*tree.Partition) error { 187 for _, def := range defs { 188 if partitionInfo.PartitionColumns != nil && len(partitionInfo.PartitionColumns.Columns) > 0 { 189 if err := checkRangeColumnsTypeAndValuesMatch(binder, partitionInfo, def); err != nil { 190 return err 191 } 192 } else { 193 if err := checkPartitionValuesIsInt(binder, def, partitionInfo); err != nil { 194 return err 195 } 196 } 197 } 198 return nil 199 } 200 201 func checkRangeColumnsTypeAndValuesMatch(binder *PartitionBinder, partitionInfo *plan.PartitionByDef, partition *tree.Partition) error { 202 if valuesLessThan, ok := partition.Values.(*tree.ValuesLessThan); ok { 203 exprs := valuesLessThan.ValueList 204 // Validate() has already checked len(colNames) = len(exprs) 205 // create table ... partition by range columns (cols) 206 // partition p0 values less than (expr) 207 // check the type of cols[i] and expr is consistent. 208 colTypes := collectColumnsType(partitionInfo) 209 for i, colExpr := range exprs { 210 if _, ok1 := colExpr.(*tree.MaxValue); ok1 { 211 continue 212 } 213 colType := colTypes[i] 214 val, err := binder.BindExpr(colExpr, 0, true) 215 if err != nil { 216 return err 217 } 218 switch val.Expr.(type) { 219 case *plan.Expr_C, *plan.Expr_Max: 220 case *plan.Expr_F: 221 default: 222 return moerr.NewInternalError(binder.GetContext(), "This partition function is not allowed") 223 } 224 225 // Check val.ConvertTo(colType) doesn't work, so we need this case by case check. 226 vkind := val.Typ 227 switch types.T(colType.Id) { 228 case types.T_date, types.T_datetime: 229 switch types.T(vkind.Id) { 230 case types.T_varchar, types.T_char: 231 default: 232 return moerr.NewInternalError(binder.GetContext(), "Partition column values of incorrect type") 233 } 234 case types.T_int8, types.T_int16, types.T_int32, types.T_int64, types.T_uint8, types.T_uint16, types.T_uint32, types.T_uint64: 235 switch types.T(vkind.Id) { 236 case types.T_int8, types.T_int16, types.T_int32, types.T_int64, types.T_uint8, types.T_uint16, types.T_uint32, types.T_uint64: //+types.T_null: 237 default: 238 return moerr.NewInternalError(binder.GetContext(), "Partition column values of incorrect type") 239 } 240 case types.T_float32, types.T_float64: 241 switch types.T(vkind.Id) { 242 case types.T_float32, types.T_float64: //+types.T_null: 243 default: 244 return moerr.NewInternalError(binder.GetContext(), "Partition column values of incorrect type") 245 } 246 case types.T_varchar, types.T_char: 247 switch types.T(vkind.Id) { 248 case types.T_varchar, types.T_char: //+types.T_null: 249 default: 250 return moerr.NewInternalError(binder.GetContext(), "Partition column values of incorrect type") 251 } 252 } 253 } 254 return nil 255 } else { 256 return moerr.NewInternalError(binder.GetContext(), "list partition function is not values in expression") 257 } 258 } 259 260 func checkPartitionValuesIsInt(binder *PartitionBinder, partition *tree.Partition, info *plan.PartitionByDef) error { 261 unsignedFlag := types.IsUnsignedInt(types.T(info.PartitionExpr.Expr.Typ.Id)) 262 if valuesLess, ok := partition.Values.(*tree.ValuesLessThan); ok { 263 exprs := valuesLess.ValueList 264 for _, exp := range exprs { 265 if _, ok := exp.(*tree.MaxValue); ok { 266 continue 267 } 268 val, err := binder.BindExpr(exp, 0, true) 269 if err != nil { 270 return err 271 } 272 273 compilerContext := binder.builder.compCtx 274 evalExpr, err := EvalPlanExpr(binder.GetContext(), val, compilerContext.GetProcess()) 275 if err != nil { 276 return err 277 } 278 279 cval, ok1 := evalExpr.Expr.(*plan.Expr_C) 280 if !ok1 { 281 return moerr.NewInternalError(binder.GetContext(), "This partition function is not allowed") 282 } 283 284 switch types.T(evalExpr.Typ.Id) { 285 case types.T_uint8, types.T_uint16, types.T_uint32, types.T_uint64, types.T_any: 286 case types.T_int8, types.T_int16, types.T_int32, types.T_int64: 287 switch value := cval.C.Value.(type) { 288 case *plan.Const_I8Val: 289 if value.I8Val < 0 && unsignedFlag { 290 return moerr.NewInternalError(binder.GetContext(), "Partition constant is out of partition function domain") 291 } 292 case *plan.Const_I16Val: 293 if value.I16Val < 0 && unsignedFlag { 294 return moerr.NewInternalError(binder.GetContext(), "Partition constant is out of partition function domain") 295 } 296 case *plan.Const_I32Val: 297 if value.I32Val < 0 && unsignedFlag { 298 return moerr.NewInternalError(binder.GetContext(), "Partition constant is out of partition function domain") 299 } 300 case *plan.Const_I64Val: 301 if value.I64Val < 0 && unsignedFlag { 302 return moerr.NewInternalError(binder.GetContext(), "Partition constant is out of partition function domain") 303 } 304 default: 305 return moerr.NewInternalError(binder.GetContext(), "VALUES value for partition '%-.64s' must have type INT", partition.Name) 306 } 307 default: 308 return moerr.NewInternalError(binder.GetContext(), "VALUES value for partition '%-.64s' must have type INT", partition.Name) 309 } 310 } 311 } 312 return nil 313 } 314 315 // checkPartitionByList checks validity of list partition. 316 func checkPartitionByList(partitionBinder *PartitionBinder, partitionInfo *plan.PartitionByDef, tableDef *TableDef) error { 317 return checkListPartitionValue(partitionBinder, partitionInfo, tableDef) 318 } 319 320 func checkListPartitionValue(partitionBinder *PartitionBinder, partitionInfo *plan.PartitionByDef, tableDef *TableDef) error { 321 //pi := tblInfo.Partition 322 ctx := partitionBinder.GetContext() 323 if len(partitionInfo.Partitions) == 0 { 324 return moerr.NewInternalError(ctx, "For %-.64s partitions each partition must be defined", "LIST") 325 } 326 expStrs, err := formatListPartitionValue(partitionBinder, partitionInfo, tableDef) 327 if err != nil { 328 return err 329 } 330 331 partitionsValuesMap := make(map[string]struct{}) 332 for _, str := range expStrs { 333 if _, ok := partitionsValuesMap[str]; ok { 334 return moerr.NewInternalError(ctx, "Multiple definition of same constant in list partitioning") 335 } 336 partitionsValuesMap[str] = struct{}{} 337 } 338 return nil 339 } 340 341 func formatListPartitionValue(binder *PartitionBinder, partitionInfo *plan.PartitionByDef, tableDef *TableDef) ([]string, error) { 342 pi := partitionInfo 343 defs := partitionInfo.Partitions 344 if pi.PartitionColumns != nil { 345 for _, column := range pi.PartitionColumns.PartitionColumns { 346 colInfo := findColumnByName(column, tableDef) 347 if colInfo == nil { 348 return nil, moerr.NewInternalError(binder.GetContext(), "Field in list of fields for partition function not found in table") 349 } 350 } 351 } 352 353 exprStrs := make([]string, 0) 354 inValueStrs := make([]string, 0) 355 for i := range defs { 356 inValueStrs = inValueStrs[:0] 357 for _, val := range defs[i].InValues { 358 compilerContext := binder.builder.compCtx 359 evalExpr, err := EvalPlanExpr(binder.GetContext(), val, compilerContext.GetProcess()) 360 if err != nil { 361 return nil, err 362 } 363 364 cval, ok1 := evalExpr.Expr.(*plan.Expr_C) 365 if !ok1 { 366 return nil, moerr.NewInternalError(binder.GetContext(), "This partition function is not allowed") 367 } 368 s := cval.C.String() 369 inValueStrs = append(inValueStrs, s) 370 } 371 exprStrs = append(exprStrs, inValueStrs...) 372 } 373 return exprStrs, nil 374 } 375 376 func collectColumnsType(partitionInfo *plan.PartitionByDef) []*Type { 377 if len(partitionInfo.PartitionColumns.Columns) > 0 { 378 colTypes := make([]*Type, 0, len(partitionInfo.PartitionColumns.Columns)) 379 for _, col := range partitionInfo.PartitionColumns.Columns { 380 colTypes = append(colTypes, col.Typ) 381 } 382 return colTypes 383 } 384 return nil 385 } 386 387 func findColumnByName(colName string, tbdef *TableDef) *ColDef { 388 if tbdef == nil { 389 return nil 390 } 391 for _, colDef := range tbdef.Cols { 392 if colDef.Name == colName { 393 return colDef 394 } 395 } 396 return nil 397 } 398 399 func EvalPlanExpr(ctx context.Context, expr *plan.Expr, process *process.Process) (*plan.Expr, error) { 400 switch expr.Expr.(type) { 401 case *plan.Expr_C: 402 return expr, nil 403 default: 404 // try to calculate default value, return err if fails 405 bat := batch.NewWithSize(0) 406 bat.Zs = []int64{1} 407 newExpr, err := ConstantFold(bat, expr, process) 408 if err != nil { 409 return nil, err 410 } 411 if _, ok := newExpr.Expr.(*plan.Expr_C); ok { 412 return newExpr, nil 413 } else { 414 return nil, moerr.NewInternalError(ctx, "This partition function is not allowed") 415 } 416 417 } 418 } 419 420 // Continue to use this function in the future. Do not delete this function temporarily. Please call @qingxinhome 421 /* 422 // checkPartitionFuncValid checks partition function validly. 423 func checkPartitionFuncValid(ctx context.Context, tbdef *TableDef, partby tree.PartitionBy) error { 424 if partby.PType == nil { 425 return nil 426 } 427 428 checker := &partitionExprChecker{ 429 processors: []partitionExprProcessor{checkPartitionExprAllowed}, 430 tbdef: tbdef, 431 err: nil, 432 } 433 434 switch partitionType := partby.PType.(type) { 435 case *tree.KeyType: 436 if partitionType.ColumnList != nil { 437 for _, expr := range partitionType.ColumnList { 438 PartitionExprSemanticCheck(ctx, tbdef, expr, checker) 439 if checker.err != nil { 440 return checker.err 441 } 442 } 443 } 444 case *tree.HashType: 445 PartitionExprSemanticCheck(ctx, tbdef, partitionType.Expr, checker) 446 if checker.err != nil { 447 return checker.err 448 } 449 case *tree.RangeType: 450 if partitionType.ColumnList != nil { 451 for _, expr := range partitionType.ColumnList { 452 PartitionExprSemanticCheck(ctx, tbdef, expr, checker) 453 if checker.err != nil { 454 return checker.err 455 } 456 } 457 } else { 458 PartitionExprSemanticCheck(ctx, tbdef, partitionType.Expr, checker) 459 if checker.err != nil { 460 return checker.err 461 } 462 } 463 case *tree.ListType: 464 if partitionType.ColumnList != nil { 465 for _, expr := range partitionType.ColumnList { 466 PartitionExprSemanticCheck(ctx, tbdef, expr, checker) 467 if checker.err != nil { 468 return checker.err 469 } 470 } 471 } else { 472 PartitionExprSemanticCheck(ctx, tbdef, partitionType.Expr, checker) 473 if checker.err != nil { 474 return checker.err 475 } 476 } 477 } 478 return nil 479 } 480 481 type partitionExprProcessor func(ctx context.Context, def *TableDef, expr tree.Expr) error 482 type partitionExprChecker struct { 483 processors []partitionExprProcessor 484 tbdef *TableDef 485 err error 486 } 487 488 func PartitionExprSemanticCheck(ctx context.Context, tbdef *TableDef, expr tree.Expr, checker *partitionExprChecker) (canNext bool) { 489 for _, processor := range checker.processors { 490 if err := processor(ctx, tbdef, expr); err != nil { 491 checker.err = err 492 return false 493 } 494 } 495 496 switch v := expr.(type) { 497 case *tree.FuncExpr: 498 for _, e := range v.Exprs { 499 next := PartitionExprSemanticCheck(ctx, tbdef, e, checker) 500 if !next { 501 return next 502 } 503 } 504 case *tree.BinaryExpr: 505 next := PartitionExprSemanticCheck(ctx, tbdef, v.Left, checker) 506 if !next { 507 return next 508 } 509 510 next = PartitionExprSemanticCheck(ctx, tbdef, v.Right, checker) 511 if !next { 512 return next 513 } 514 case *tree.UnaryExpr: 515 next := PartitionExprSemanticCheck(ctx, tbdef, v.Expr, checker) 516 if !next { 517 return next 518 } 519 case *tree.ParenExpr: 520 next := PartitionExprSemanticCheck(ctx, tbdef, v.Expr, checker) 521 if !next { 522 return next 523 } 524 case *tree.UnresolvedName: 525 return false 526 case *tree.MaxValue: 527 return false 528 default: 529 checker.err = moerr.NewInternalError(ctx, "This partition function is not allowed") 530 return false 531 } 532 return true 533 } 534 535 func checkPartitionExprAllowed(ctx context.Context, tb *TableDef, e tree.Expr) error { 536 switch v := e.(type) { 537 case *tree.FuncExpr: 538 funcRef, ok := v.Func.FunctionReference.(*tree.UnresolvedName) 539 if !ok { 540 return moerr.NewNYI(ctx, "function expr '%v'", v) 541 } 542 funcName := funcRef.Parts[0] 543 if _, ok = AllowedPartitionFuncMap[funcName]; ok { 544 return nil 545 } 546 case *tree.BinaryExpr: 547 if _, ok := AllowedPartition4BinaryOpMap[v.Op]; ok { 548 return checkNoTimestampArgs(ctx, tb, v.Left, v.Right) 549 } 550 case *tree.UnaryExpr: 551 if _, ok := AllowedPartition4UnaryOpMap[v.Op]; ok { 552 return checkNoTimestampArgs(ctx, tb, v.Expr) 553 } 554 case *tree.ParenExpr, *tree.MaxValue, *tree.UnresolvedName: 555 return nil 556 } 557 return moerr.NewInternalError(ctx, "This partition function is not allowed") 558 } 559 560 func checkNoTimestampArgs(ctx context.Context, tbInfo *TableDef, exprs ...tree.Expr) error { 561 argsType, err := collectArgsType(ctx, tbInfo, exprs...) 562 if err != nil { 563 return err 564 } 565 if hasTimestampArgs(argsType...) { 566 return moerr.NewInternalError(ctx, "Constant, random or timezone-dependent expressions in (sub)partitioning function are not allowed") 567 } 568 return nil 569 } 570 571 func collectArgsType(ctx context.Context, tblInfo *TableDef, exprs ...tree.Expr) ([]*Type, error) { 572 ts := make([]*Type, 0, len(exprs)) 573 for _, arg := range exprs { 574 col, ok := arg.(*tree.UnresolvedName) 575 if !ok { 576 continue 577 } 578 columnInfo := findColumnByName(col.Parts[0], tblInfo) 579 if columnInfo == nil { 580 return nil, moerr.NewInternalError(ctx, "Unknown column '%-.192s' in '%-.192s'", col.Parts[0], "partition function") 581 } 582 ts = append(ts, columnInfo.GetTyp()) 583 } 584 return ts, nil 585 } 586 587 func hasDateArgs(argsType ...*Type) bool { 588 for _, t := range argsType { 589 return t.Id == int32(types.T_date) || t.Id == int32(types.T_datetime) 590 } 591 return false 592 } 593 594 func hasTimeArgs(argsType ...*Type) bool { 595 for _, t := range argsType { 596 return t.Id == int32(types.T_time) || t.Id == int32(types.T_datetime) 597 } 598 return false 599 } 600 601 func hasTimestampArgs(argsType ...*Type) bool { 602 for _, t := range argsType { 603 return t.Id == int32(types.T_timestamp) 604 } 605 return false 606 } 607 608 func hasDatetimeArgs(argsType ...*Type) bool { 609 for _, t := range argsType { 610 return t.Id == int32(types.T_datetime) 611 } 612 return false 613 } 614 */ 615 // AllowedPartitionFuncMap stores functions which can be used in the partition expression. 616 var AllowedPartitionFuncMap = map[string]int{ 617 "to_days": 1, 618 "to_seconds": 1, 619 "dayofmonth": 1, 620 "month": 1, 621 "dayofyear": 1, 622 "quarter": 1, 623 "yearweek": 1, 624 "year": 1, 625 "weekday": 1, 626 "dayofweek": 1, 627 "day": 1, 628 "hour": 1, 629 "minute": 1, 630 "second": 1, 631 "time_to_sec": 1, 632 "microsecond": 1, 633 "unix_timestamp": 1, 634 "from_days": 1, 635 "extract": 1, 636 "abs": 1, 637 "ceiling": 1, 638 "ceil": 1, 639 "datediff": 1, 640 "floor": 1, 641 "mod": 1, 642 } 643 644 // AllowedPartition4BinaryOpMap store the operator for Binary Expr 645 // link ref:https://dev.mysql.com/doc/refman/8.0/en/partitioning-limitations.html 646 var AllowedPartition4BinaryOpMap = map[tree.BinaryOp]string{ 647 tree.PLUS: "+", 648 tree.MINUS: "-", 649 tree.MULTI: "*", 650 tree.INTEGER_DIV: "div", 651 tree.MOD: "%", 652 } 653 654 // AllowedPartition4UnaryOpMap store the operator for Unary Expr 655 var AllowedPartition4UnaryOpMap = map[tree.UnaryOp]string{ 656 tree.UNARY_PLUS: "+", 657 tree.UNARY_MINUS: "-", 658 }