github.com/matrixorigin/matrixone@v1.2.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 "strings" 20 21 "github.com/matrixorigin/matrixone/pkg/catalog" 22 "github.com/matrixorigin/matrixone/pkg/common/moerr" 23 "github.com/matrixorigin/matrixone/pkg/container/batch" 24 "github.com/matrixorigin/matrixone/pkg/container/types" 25 "github.com/matrixorigin/matrixone/pkg/container/vector" 26 "github.com/matrixorigin/matrixone/pkg/pb/plan" 27 "github.com/matrixorigin/matrixone/pkg/sql/colexec" 28 "github.com/matrixorigin/matrixone/pkg/sql/parsers/tree" 29 "github.com/matrixorigin/matrixone/pkg/sql/plan/function" 30 "github.com/matrixorigin/matrixone/pkg/sql/plan/rule" 31 "github.com/matrixorigin/matrixone/pkg/vm/process" 32 ) 33 34 // add this code in buildListPartitionItem 35 // return buildListPartitionItem(partitionBinder, partitionDef, defs) 36 func buildListPartitionItem(binder *PartitionBinder, partitionDef *plan.PartitionByDef, defs []*tree.Partition) error { 37 for _, def := range defs { 38 if partitionDef.PartitionColumns != nil { 39 if err := checkListColumnsTypeAndValuesMatch(binder, partitionDef, def); err != nil { 40 return err 41 } 42 } else { 43 if err := checkListPartitionValuesIsInt(binder, def, partitionDef); err != nil { 44 return err 45 } 46 } 47 } 48 return nil 49 } 50 51 // checkPartitionExprAllowed Check whether the ast expression or sub ast expression of partition expression is used legally 52 func checkPartitionExprAllowed(ctx context.Context, tb *plan.TableDef, e tree.Expr) error { 53 switch v := e.(type) { 54 case *tree.FuncExpr: 55 funcRef, ok := v.Func.FunctionReference.(*tree.UnresolvedName) 56 if !ok { 57 return moerr.NewNYI(ctx, "invalid function expr '%v'", v) 58 } 59 funcName := strings.ToLower(funcRef.Parts[0]) 60 if _, ok := AllowedPartitionFuncMap[funcName]; ok { 61 return nil 62 } 63 64 case *tree.BinaryExpr: 65 if _, ok := AllowedPartitionBinaryOpMap[v.Op]; ok { 66 return checkNoTimestampArgs(ctx, tb, v.Left, v.Right) 67 } 68 case *tree.UnaryExpr: 69 if _, ok := AllowedPartitionUnaryOpMap[v.Op]; ok { 70 return checkNoTimestampArgs(ctx, tb, v.Expr) 71 } 72 case *tree.ParenExpr, *tree.NumVal, *tree.UnresolvedName, *tree.MaxValue: 73 return nil 74 } 75 return moerr.NewPartitionFunctionIsNotAllowed(ctx) 76 } 77 78 // checkPartitionExprArgs Check whether the parameters of the partition function are allowed 79 // see link: https://dev.mysql.com/doc/refman/8.0/en/partitioning-limitations-functions.html 80 func checkPartitionExprArgs(ctx context.Context, tblInfo *plan.TableDef, e tree.Expr) error { 81 expr, ok := e.(*tree.FuncExpr) 82 if !ok { 83 return nil 84 } 85 86 funcRef, ok := expr.Func.FunctionReference.(*tree.UnresolvedName) 87 if !ok { 88 return moerr.NewNYI(ctx, "invalid function expr '%v'", expr) 89 } 90 funcName := strings.ToLower(funcRef.Parts[0]) 91 92 argsType, err := collectArgsType(ctx, tblInfo, expr.Exprs...) 93 if err != nil { 94 return err 95 } 96 97 switch funcName { 98 case "to_days", "to_seconds", "dayofmonth", "month", "dayofyear", "quarter", "yearweek", 99 "year", "weekday", "dayofweek", "day": 100 return checkResultOK(ctx, hasDateArgs(argsType...)) 101 case "hour", "minute", "second", "time_to_sec", "microsecond": 102 return checkResultOK(ctx, hasTimeArgs(argsType...)) 103 case "unix_timestamp": 104 return checkResultOK(ctx, hasTimestampArgs(argsType...)) 105 case "from_days": 106 return checkResultOK(ctx, hasDateArgs(argsType...) || hasTimeArgs(argsType...)) 107 case "extract": 108 // see link: https://dev.mysql.com/doc/refman/8.0/en/expressions.html#temporal-intervals 109 switch strings.ToUpper(expr.Exprs[0].String()) { 110 case INTERVAL_YEAR, INTERVAL_YEAR_MONTH, INTERVAL_QUARTER, INTERVAL_MONTH, INTERVAL_DAY: 111 return checkResultOK(ctx, hasDateArgs(argsType...)) 112 case INTERVAL_DAY_MICROSECOND, INTERVAL_DAY_HOUR, INTERVAL_DAY_MINUTE, INTERVAL_DAY_SECOND: 113 return checkResultOK(ctx, hasDatetimeArgs(argsType...)) 114 case INTERVAL_HOUR, INTERVAL_HOUR_MINUTE, INTERVAL_HOUR_SECOND, INTERVAL_MINUTE, 115 INTERVAL_MINUTE_SECOND, INTERVAL_SECOND, INTERVAL_MICROSECOND, INTERVAL_HOUR_MICROSECOND, 116 INTERVAL_MINUTE_MICROSECOND, INTERVAL_SECOND_MICROSECOND: 117 return checkResultOK(ctx, hasTimeArgs(argsType...)) 118 default: 119 // EXTRACT() function with WEEK specifier. The value returned by the EXTRACT() function, 120 // when used as EXTRACT(WEEK FROM col), depends on the value of the default_week_format system variable. 121 // For this reason, EXTRACT() is not permitted as a partitioning function when it specifies the unit as WEEK. 122 return moerr.NewWrongExprInPartitionFunc(ctx) 123 } 124 case "datediff": 125 return checkResultOK(ctx, hasDateArgs(argsType...)) 126 127 case "abs", "ceiling", "floor", "mod": 128 has := hasTimestampArgs(argsType...) 129 if has { 130 return moerr.NewWrongExprInPartitionFunc(ctx) 131 } 132 } 133 return nil 134 } 135 136 // Collect the types of columns which used as the partition function parameter 137 func collectArgsType(ctx context.Context, tblInfo *plan.TableDef, exprs ...tree.Expr) ([]int32, error) { 138 types := make([]int32, 0, len(exprs)) 139 for _, arg := range exprs { 140 col, ok := arg.(*tree.UnresolvedName) 141 if !ok { 142 continue 143 } 144 145 // Check whether column name exist in the table 146 column := findColumnByName(col.Parts[0], tblInfo) 147 if column == nil { 148 return nil, moerr.NewBadFieldError(ctx, col.Parts[0], "partition function") 149 } 150 types = append(types, column.GetTyp().Id) 151 } 152 return types, nil 153 } 154 155 // hasDateArgs Check if the arguments contains date or datetime type 156 func hasDateArgs(argsType ...int32) bool { 157 for _, typeId := range argsType { 158 if typeId == int32(types.T_date) || typeId == int32(types.T_datetime) { 159 return true 160 } 161 } 162 return false 163 } 164 165 // hasTimeArgs Check if the arguments contains time or datetime type 166 func hasTimeArgs(argsType ...int32) bool { 167 for _, typeId := range argsType { 168 return typeId == int32(types.T_time) || typeId == int32(types.T_datetime) 169 } 170 return false 171 } 172 173 // hasTimestampArgs Check if the arguments contains timestamp(time zone) type 174 func hasTimestampArgs(argsType ...int32) bool { 175 for _, typeId := range argsType { 176 return typeId == int32(types.T_timestamp) 177 } 178 return false 179 } 180 181 // hasTimestampArgs Check if the arguments contains datetime type 182 func hasDatetimeArgs(argsType ...int32) bool { 183 for _, typeId := range argsType { 184 return typeId == int32(types.T_datetime) 185 } 186 return false 187 188 } 189 190 // checkNoTimestampArgs Check to confirm that there are no timestamp type arguments in the partition expression 191 func checkNoTimestampArgs(ctx context.Context, tbInfo *plan.TableDef, exprs ...tree.Expr) error { 192 argsType, err := collectArgsType(ctx, tbInfo, exprs...) 193 if err != nil { 194 return err 195 } 196 if hasTimestampArgs(argsType...) { 197 return moerr.NewWrongExprInPartitionFunc(ctx) 198 } 199 return nil 200 } 201 202 // checkResultOK For partition table in mysql, Constant, random or timezone-dependent expressions in (sub)partitioning function are not allowed 203 func checkResultOK(ctx context.Context, ok bool) error { 204 if !ok { 205 return moerr.NewWrongExprInPartitionFunc(ctx) 206 } 207 return nil 208 } 209 210 func checkListColumnsTypeAndValuesMatch(binder *PartitionBinder, partitionDef *plan.PartitionByDef, partition *tree.Partition) error { 211 colTypes := collectColumnsType(partitionDef) 212 213 inClause := partition.Values.(*tree.ValuesIn) 214 for _, inValue := range inClause.ValueList { 215 expr, err := binder.BindExpr(inValue, 0, true) 216 if err != nil { 217 return err 218 } 219 220 switch expr.Expr.(type) { 221 case *plan.Expr_List: 222 tuple := expr.Expr.(*plan.Expr_List) 223 if len(colTypes) != len(tuple.List.List) { 224 return moerr.NewErrPartitionColumnList(binder.GetContext()) 225 } 226 227 for i, colExpr := range tuple.List.List { 228 if err := checkPartitionColumnValue(binder, colTypes[i], colExpr); err != nil { 229 return err 230 } 231 } 232 case *plan.Expr_Lit, *plan.Expr_F: 233 if len(colTypes) != 1 { 234 return moerr.NewErrPartitionColumnList(binder.GetContext()) 235 } 236 237 if err := checkPartitionColumnValue(binder, colTypes[0], expr); err != nil { 238 return err 239 } 240 case *plan.Expr_Max: 241 return moerr.NewErrMaxvalueInValuesIn(binder.GetContext()) 242 default: 243 return moerr.NewPartitionFunctionIsNotAllowed(binder.GetContext()) 244 } 245 } 246 return nil 247 } 248 249 // checkPartitionColumnValue check whether the types of partition column and partition value match 250 func checkPartitionColumnValue(binder *PartitionBinder, colType Type, colExpr *plan.Expr) error { 251 val, err := EvalPlanExpr(binder.GetContext(), colExpr, binder.builder.compCtx.GetProcess()) 252 if err != nil { 253 return err 254 } 255 256 vkind := types.T(val.Typ.Id) 257 258 switch types.T(colType.Id) { 259 case types.T_date, types.T_datetime, types.T_time: 260 switch vkind { 261 case types.T_varchar, types.T_char, types.T_any: 262 default: 263 return moerr.NewWrongTypeColumnValue(binder.GetContext()) 264 } 265 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_bit: 266 switch vkind { 267 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_bit, types.T_any: 268 default: 269 return moerr.NewWrongTypeColumnValue(binder.GetContext()) 270 } 271 case types.T_float32, types.T_float64: 272 switch vkind { 273 case types.T_float32, types.T_float64, types.T_any: 274 default: 275 return moerr.NewWrongTypeColumnValue(binder.GetContext()) 276 } 277 case types.T_varchar, types.T_char: 278 switch vkind { 279 case types.T_varchar, types.T_char, types.T_any: 280 default: 281 return moerr.NewWrongTypeColumnValue(binder.GetContext()) 282 } 283 } 284 285 if castExpr, err := forceCastExpr(binder.GetContext(), val, colType); err != nil { 286 return err 287 } else { 288 if castVal, err := EvalPlanExpr(binder.GetContext(), castExpr, binder.builder.compCtx.GetProcess()); err != nil { 289 return moerr.NewWrongTypeColumnValue(binder.GetContext()) 290 } else { 291 if _, ok := castVal.Expr.(*plan.Expr_Lit); !ok { 292 return moerr.NewPartitionFunctionIsNotAllowed(binder.GetContext()) 293 } 294 } 295 } 296 return nil 297 } 298 299 func checkListPartitionValuesIsInt(binder *PartitionBinder, partition *tree.Partition, info *plan.PartitionByDef) error { 300 unsignedFlag := types.T(info.PartitionExpr.Expr.Typ.Id).IsUnsignedInt() 301 inClause := partition.Values.(*tree.ValuesIn) 302 for _, inValue := range inClause.ValueList { 303 expr, err := binder.BindExpr(inValue, 0, true) 304 if err != nil { 305 return err 306 } 307 switch expr.Expr.(type) { 308 case *plan.Expr_Lit, *plan.Expr_F: 309 evalExpr, err := EvalPlanExpr(binder.GetContext(), expr, binder.builder.compCtx.GetProcess()) 310 if err != nil { 311 return err 312 } 313 314 cval, ok1 := evalExpr.Expr.(*plan.Expr_Lit) 315 if !ok1 { 316 return moerr.NewPartitionFunctionIsNotAllowed(binder.GetContext()) 317 } 318 319 switch types.T(evalExpr.Typ.Id) { 320 case types.T_uint8, types.T_uint16, types.T_uint32, types.T_uint64, types.T_bit, types.T_any: 321 case types.T_int8, types.T_int16, types.T_int32, types.T_int64: 322 switch value := cval.Lit.Value.(type) { 323 case *plan.Literal_I8Val: 324 if value.I8Val < 0 && unsignedFlag { 325 //return moerr.NewInternalError(binder.GetContext(), "Partition constant is out of partition function domain") 326 return moerr.NewPartitionConstDomain(binder.GetContext()) 327 } 328 case *plan.Literal_I16Val: 329 if value.I16Val < 0 && unsignedFlag { 330 //return moerr.NewInternalError(binder.GetContext(), "Partition constant is out of partition function domain") 331 return moerr.NewPartitionConstDomain(binder.GetContext()) 332 } 333 case *plan.Literal_I32Val: 334 if value.I32Val < 0 && unsignedFlag { 335 //return moerr.NewInternalError(binder.GetContext(), "Partition constant is out of partition function domain") 336 return moerr.NewPartitionConstDomain(binder.GetContext()) 337 } 338 case *plan.Literal_I64Val: 339 if value.I64Val < 0 && unsignedFlag { 340 //return moerr.NewInternalError(binder.GetContext(), "Partition constant is out of partition function domain") 341 return moerr.NewPartitionConstDomain(binder.GetContext()) 342 } 343 default: 344 return moerr.NewValuesIsNotIntType(binder.GetContext(), partition.Name) 345 } 346 default: 347 return moerr.NewValuesIsNotIntType(binder.GetContext(), partition.Name) 348 } 349 case *plan.Expr_List: 350 return moerr.NewErrRowSinglePartitionField(binder.GetContext()) 351 case *plan.Expr_Max: 352 return moerr.NewErrMaxvalueInValuesIn(binder.GetContext()) 353 default: 354 return moerr.NewPartitionFunctionIsNotAllowed(binder.GetContext()) 355 } 356 } 357 return nil 358 } 359 360 // add this code in buildRangePartitionDefinitions 361 // return buildRangePartitionDefinitionItem(partitionBinder, partitionDef, defs) 362 func buildRangePartitionItem(binder *PartitionBinder, partitionDef *plan.PartitionByDef, defs []*tree.Partition) error { 363 for _, def := range defs { 364 if partitionDef.PartitionColumns != nil && len(partitionDef.PartitionColumns.Columns) > 0 { 365 if err := checkRangeColumnsTypeAndValuesMatch(binder, partitionDef, def); err != nil { 366 return err 367 } 368 } else { 369 if err := checkPartitionValuesIsInt(binder, def, partitionDef); err != nil { 370 return err 371 } 372 } 373 } 374 return nil 375 } 376 377 // 我认为这里的PartitionBinder是一个独立的binder,不需要表的上下文信息,可以单独实例化 378 func checkRangeColumnsTypeAndValuesMatch(binder *PartitionBinder, partitionDef *plan.PartitionByDef, partition *tree.Partition) error { 379 if valuesLessThan, ok := partition.Values.(*tree.ValuesLessThan); ok { 380 exprs := valuesLessThan.ValueList 381 // Validate() has already checked len(colNames) = len(exprs) 382 // create table ... partition by range columns (cols) 383 // partition p0 values less than (expr) 384 // check the type of cols[i] and expr is consistent. 385 colTypes := collectColumnsType(partitionDef) 386 for i, colExpr := range exprs { 387 if _, ok1 := colExpr.(*tree.MaxValue); ok1 { 388 continue 389 } 390 colType := colTypes[i] 391 val, err := binder.BindExpr(colExpr, 0, true) 392 if err != nil { 393 return err 394 } 395 switch val.Expr.(type) { 396 case *plan.Expr_Lit, *plan.Expr_Max: 397 case *plan.Expr_F: 398 default: 399 //return moerr.NewInternalError(binder.GetContext(), "This partition function is not allowed") 400 return moerr.NewPartitionFunctionIsNotAllowed(binder.GetContext()) 401 } 402 403 // Check val.ConvertTo(colType) doesn't work, so we need this case by case check. 404 vkind := val.Typ 405 switch types.T(colType.Id) { 406 case types.T_date, types.T_datetime: 407 switch types.T(vkind.Id) { 408 case types.T_varchar, types.T_char: 409 default: 410 //return moerr.NewInternalError(binder.GetContext(), "Partition column values of incorrect type") 411 return moerr.NewWrongTypeColumnValue(binder.GetContext()) 412 } 413 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_bit: 414 switch types.T(vkind.Id) { 415 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_bit: //+types.T_null: 416 default: 417 //return moerr.NewInternalError(binder.GetContext(), "Partition column values of incorrect type") 418 return moerr.NewWrongTypeColumnValue(binder.GetContext()) 419 } 420 case types.T_float32, types.T_float64: 421 switch types.T(vkind.Id) { 422 case types.T_float32, types.T_float64: //+types.T_null: 423 default: 424 //return moerr.NewInternalError(binder.GetContext(), "Partition column values of incorrect type") 425 return moerr.NewWrongTypeColumnValue(binder.GetContext()) 426 } 427 case types.T_varchar, types.T_char: 428 switch types.T(vkind.Id) { 429 case types.T_varchar, types.T_char: //+types.T_null: 430 default: 431 //return moerr.NewInternalError(binder.GetContext(), "Partition column values of incorrect type") 432 return moerr.NewWrongTypeColumnValue(binder.GetContext()) 433 } 434 } 435 } 436 return nil 437 } else { 438 return moerr.NewInternalError(binder.GetContext(), "list partition function is not values in expression") 439 } 440 } 441 442 func checkPartitionValuesIsInt(binder *PartitionBinder, partition *tree.Partition, info *plan.PartitionByDef) error { 443 unsignedFlag := types.T(info.PartitionExpr.Expr.Typ.Id).IsUnsignedInt() 444 if valuesLess, ok := partition.Values.(*tree.ValuesLessThan); ok { 445 exprs := valuesLess.ValueList 446 for _, exp := range exprs { 447 if _, ok := exp.(*tree.MaxValue); ok { 448 continue 449 } 450 val, err := binder.BindExpr(exp, 0, true) 451 if err != nil { 452 return err 453 } 454 455 compilerContext := binder.builder.compCtx 456 evalExpr, err := EvalPlanExpr(binder.GetContext(), val, compilerContext.GetProcess()) 457 if err != nil { 458 return err 459 } 460 461 cval, ok1 := evalExpr.Expr.(*plan.Expr_Lit) 462 if !ok1 { 463 //return moerr.NewInternalError(binder.GetContext(), "This partition function is not allowed") 464 return moerr.NewPartitionFunctionIsNotAllowed(binder.GetContext()) 465 } 466 467 switch types.T(evalExpr.Typ.Id) { 468 case types.T_uint8, types.T_uint16, types.T_uint32, types.T_uint64, types.T_bit, types.T_any: 469 case types.T_int8, types.T_int16, types.T_int32, types.T_int64: 470 switch value := cval.Lit.Value.(type) { 471 case *plan.Literal_I8Val: 472 if value.I8Val < 0 && unsignedFlag { 473 //return moerr.NewInternalError(binder.GetContext(), "Partition constant is out of partition function domain") 474 return moerr.NewPartitionConstDomain(binder.GetContext()) 475 } 476 case *plan.Literal_I16Val: 477 if value.I16Val < 0 && unsignedFlag { 478 //return moerr.NewInternalError(binder.GetContext(), "Partition constant is out of partition function domain") 479 return moerr.NewPartitionConstDomain(binder.GetContext()) 480 } 481 case *plan.Literal_I32Val: 482 if value.I32Val < 0 && unsignedFlag { 483 //return moerr.NewInternalError(binder.GetContext(), "Partition constant is out of partition function domain") 484 return moerr.NewPartitionConstDomain(binder.GetContext()) 485 } 486 case *plan.Literal_I64Val: 487 if value.I64Val < 0 && unsignedFlag { 488 //return moerr.NewInternalError(binder.GetContext(), "Partition constant is out of partition function domain") 489 return moerr.NewPartitionConstDomain(binder.GetContext()) 490 } 491 default: 492 //return moerr.NewInternalError(binder.GetContext(), "VALUES value for partition '%-.64s' must have type INT", partition.Name) 493 return moerr.NewValuesIsNotIntType(binder.GetContext(), partition.Name) 494 } 495 default: 496 //return moerr.NewInternalError(binder.GetContext(), "VALUES value for partition '%-.64s' must have type INT", partition.Name) 497 return moerr.NewValuesIsNotIntType(binder.GetContext(), partition.Name) 498 } 499 } 500 } 501 return nil 502 } 503 504 // checkPartitionByList checks validity of list partition. 505 func checkPartitionByList(partitionBinder *PartitionBinder, partitionDef *plan.PartitionByDef, tableDef *TableDef) error { 506 return checkListPartitionValue(partitionBinder, partitionDef, tableDef) 507 } 508 509 // checkListPartitionValue Check if the definition of the list partition entry is correct 510 func checkListPartitionValue(partitionBinder *PartitionBinder, partitionDef *plan.PartitionByDef, tableDef *TableDef) error { 511 ctx := partitionBinder.GetContext() 512 if len(partitionDef.Partitions) == 0 { 513 return moerr.NewPartitionsMustBeDefined(ctx, "LIST") 514 } 515 516 expStrs, err := formatListPartitionValue(partitionBinder, tableDef, partitionDef) 517 if err != nil { 518 return err 519 } 520 521 partitionsValuesMap := make(map[string]struct{}) 522 for _, str := range expStrs { 523 if _, ok := partitionsValuesMap[str]; ok { 524 return moerr.NewMultipleDefConstInListPart(ctx) 525 } 526 partitionsValuesMap[str] = struct{}{} 527 } 528 return nil 529 } 530 531 func formatListPartitionValue(binder *PartitionBinder, tblInfo *TableDef, pi *plan.PartitionByDef) ([]string, error) { 532 defs := pi.Partitions 533 var colTps []Type 534 if pi.PartitionExpr != nil { 535 tp := types.T_int64 536 if isPartExprUnsigned(pi) { 537 tp = types.T_uint64 538 } 539 toType := tp.ToType() 540 makePlan2Type(&toType) 541 colTps = []Type{makePlan2Type(&toType)} 542 } else { 543 colTps = make([]Type, 0, len(pi.PartitionColumns.PartitionColumns)) 544 for _, colName := range pi.PartitionColumns.PartitionColumns { 545 colInfo := findColumnByName(colName, tblInfo) 546 if colInfo == nil { 547 return nil, moerr.NewFieldNotFoundPart(binder.GetContext()) 548 } 549 colTps = append(colTps, colInfo.Typ) 550 } 551 } 552 553 exprStrs := make([]string, 0) 554 inValueStrs := make([]string, 0) 555 compilerContext := binder.builder.compCtx 556 for _, def := range defs { 557 for _, val := range def.InValues { 558 inValueStrs = inValueStrs[:0] 559 switch val.Expr.(type) { 560 case *plan.Expr_List: 561 temp := "" 562 expr := val.Expr.(*plan.Expr_List) 563 for k, elem := range expr.List.List { 564 if elemStr, err := evalPartitionFieldExpr(binder.GetContext(), compilerContext.GetProcess(), colTps[k], elem); err != nil { 565 return nil, err 566 } else { 567 if k == 0 { 568 temp += "(" + elemStr 569 } else { 570 temp += "," + elemStr 571 } 572 } 573 } 574 temp += ")" 575 inValueStrs = append(inValueStrs, temp) 576 default: 577 if exprStr, err := evalPartitionFieldExpr(binder.GetContext(), compilerContext.GetProcess(), colTps[0], val); err != nil { 578 return nil, err 579 } else { 580 inValueStrs = append(inValueStrs, exprStr) 581 } 582 } 583 exprStrs = append(exprStrs, strings.Join(inValueStrs, ",")) 584 } 585 } 586 return exprStrs, nil 587 } 588 589 func isPartExprUnsigned(pi *plan.PartitionByDef) bool { 590 return types.T(pi.PartitionExpr.Expr.Typ.Id).IsUnsignedInt() 591 } 592 593 func evalPartitionFieldExpr(ctx context.Context, process *process.Process, colType Type, colExpr *plan.Expr) (string, error) { 594 evalExpr, err := EvalPlanExpr(ctx, colExpr, process) 595 if err != nil { 596 return "", err 597 } 598 599 castExpr, err := forceCastExpr(ctx, evalExpr, colType) 600 if err != nil { 601 return "", err 602 } 603 604 castVal, err := EvalPlanExpr(ctx, castExpr, process) 605 if err != nil { 606 return "", moerr.NewWrongTypeColumnValue(ctx) 607 } 608 609 if cval, ok := castVal.Expr.(*plan.Expr_Lit); ok { 610 return cval.Lit.String(), nil 611 } else { 612 return "", moerr.NewPartitionFunctionIsNotAllowed(ctx) 613 } 614 } 615 616 // collectPartitionColumnsType 617 func collectColumnsType(partitionDef *plan.PartitionByDef) []Type { 618 if len(partitionDef.PartitionColumns.Columns) > 0 { 619 colTypes := make([]Type, 0, len(partitionDef.PartitionColumns.Columns)) 620 for _, col := range partitionDef.PartitionColumns.Columns { 621 colTypes = append(colTypes, col.Typ) 622 } 623 return colTypes 624 } 625 return nil 626 } 627 628 func findColumnByName(colName string, tbdef *TableDef) *ColDef { 629 if tbdef == nil { 630 return nil 631 } 632 for _, colDef := range tbdef.Cols { 633 if colDef.Name == colName { 634 return colDef 635 } 636 } 637 return nil 638 } 639 640 func EvalPlanExpr(ctx context.Context, expr *plan.Expr, process *process.Process) (*plan.Expr, error) { 641 switch expr.Expr.(type) { 642 case *plan.Expr_Lit: 643 return expr, nil 644 default: 645 // try to calculate default value, return err if fails 646 newExpr, err := PartitionFuncConstantFold(batch.EmptyForConstFoldBatch, expr, process) 647 if err != nil { 648 return nil, err 649 } 650 if _, ok := newExpr.Expr.(*plan.Expr_Lit); ok { 651 return newExpr, nil 652 } else { 653 //return nil, moerr.NewInternalError(ctx, "This partition function is not allowed") 654 return nil, moerr.NewPartitionFunctionIsNotAllowed(ctx) 655 } 656 657 } 658 } 659 660 func PartitionFuncConstantFold(bat *batch.Batch, e *plan.Expr, proc *process.Process) (*plan.Expr, error) { 661 // If it is Expr_List, perform constant folding on its elements 662 if exprImpl, ok := e.Expr.(*plan.Expr_List); ok { 663 exprList := exprImpl.List.List 664 for i := range exprList { 665 foldExpr, err := PartitionFuncConstantFold(bat, exprList[i], proc) 666 if err != nil { 667 return e, nil 668 } 669 exprList[i] = foldExpr 670 } 671 672 vec, err := colexec.GenerateConstListExpressionExecutor(proc, exprList) 673 if err != nil { 674 return nil, err 675 } 676 defer vec.Free(proc.Mp()) 677 678 vec.InplaceSortAndCompact() 679 data, err := vec.MarshalBinary() 680 if err != nil { 681 return nil, err 682 } 683 684 return &plan.Expr{ 685 Typ: e.Typ, 686 Expr: &plan.Expr_Vec{ 687 Vec: &plan.LiteralVec{ 688 Len: int32(vec.Length()), 689 Data: data, 690 }, 691 }, 692 }, nil 693 } 694 695 ef, ok := e.Expr.(*plan.Expr_F) 696 if !ok || proc == nil { 697 return e, nil 698 } 699 700 overloadID := ef.F.Func.GetObj() 701 f, err := function.GetFunctionById(proc.Ctx, overloadID) 702 if err != nil { 703 return nil, err 704 } 705 if ef.F.Func.ObjName != "cast" && f.CannotFold() { // function cannot be fold 706 return e, nil 707 } 708 for i := range ef.F.Args { 709 if ef.F.Args[i], err = PartitionFuncConstantFold(bat, ef.F.Args[i], proc); err != nil { 710 return nil, err 711 } 712 } 713 if !rule.IsConstant(e, false) { 714 return e, nil 715 } 716 717 vec, err := colexec.EvalExpressionOnce(proc, e, []*batch.Batch{bat}) 718 if err != nil { 719 return nil, err 720 } 721 defer vec.Free(proc.Mp()) 722 c := rule.GetConstantValue(vec, true, 0) 723 if c == nil { 724 return e, nil 725 } 726 e.Expr = &plan.Expr_Lit{ 727 Lit: c, 728 } 729 return e, nil 730 } 731 732 // AllowedPartitionFuncMap stores functions which can be used in the partition expression. 733 // See Link: https://dev.mysql.com/doc/refman/8.0/en/partitioning-limitations-functions.html 734 var AllowedPartitionFuncMap = map[string]int{ 735 "to_days": 1, 736 "to_seconds": 1, 737 "dayofmonth": 1, 738 "month": 1, 739 "dayofyear": 1, 740 "quarter": 1, 741 "yearweek": 1, 742 "year": 1, 743 "weekday": 1, 744 "dayofweek": 1, 745 "day": 1, 746 "hour": 1, 747 "minute": 1, 748 "second": 1, 749 "time_to_sec": 1, 750 "microsecond": 1, 751 "unix_timestamp": 1, 752 "from_days": 1, 753 "extract": 1, 754 "abs": 1, 755 "ceiling": 1, 756 "ceil": 1, 757 "datediff": 1, 758 "floor": 1, 759 "mod": 1, 760 } 761 762 // AllowedPartitionBinaryOpMap store the operators of Binary operation expression 763 // link ref:https://dev.mysql.com/doc/refman/8.0/en/partitioning-limitations.html 764 var AllowedPartitionBinaryOpMap = map[tree.BinaryOp]string{ 765 tree.PLUS: "+", 766 tree.MINUS: "-", 767 tree.MULTI: "*", 768 tree.INTEGER_DIV: "div", 769 tree.MOD: "%", 770 } 771 772 // AllowedPartitionUnaryOpMap store the operators of Unary expression 773 // link ref:https://dev.mysql.com/doc/refman/8.0/en/partitioning-limitations.html 774 var AllowedPartitionUnaryOpMap = map[tree.UnaryOp]string{ 775 tree.UNARY_PLUS: "+", 776 tree.UNARY_MINUS: "-", 777 } 778 779 // The following code shows the expected form of the expr argument for each unit value. 780 // see link: https://dev.mysql.com/doc/refman/8.0/en/expressions.html#temporal-intervals 781 const ( 782 // INTERVAL_MICROSECOND is the time or timestamp unit MICROSECOND. 783 INTERVAL_MICROSECOND = "MICROSECOND" 784 // INTERVAL_SECOND is the time or timestamp unit SECOND. 785 INTERVAL_SECOND = "SECOND" 786 // INTERVAL_MINUTE is the time or timestamp unit MINUTE. 787 INTERVAL_MINUTE = "MINUTE" 788 // INTERVAL_HOUR is the time or timestamp unit HOUR. 789 INTERVAL_HOUR = "HOUR" 790 // INTERVAL_DAY is the time or timestamp unit DAY. 791 INTERVAL_DAY = "DAY" 792 // INTERVAL_WEEK is the time or timestamp unit WEEK. 793 INTERVAL_WEEK = "WEEK" 794 // INTERVAL_MONTH is the time or timestamp unit MONTH. 795 INTERVAL_MONTH = "MONTH" 796 // INTERVAL_QUARTER is the time or timestamp unit QUARTER. 797 INTERVAL_QUARTER = "QUARTER" 798 // INTERVAL_YEAR is the time or timestamp unit YEAR. 799 INTERVAL_YEAR = "YEAR" 800 // INTERVAL_SECOND_MICROSECOND is the time unit SECOND_MICROSECOND. 801 INTERVAL_SECOND_MICROSECOND = "SECOND_MICROSECOND" 802 // INTERVAL_MINUTE_MICROSECOND is the time unit MINUTE_MICROSECOND. 803 INTERVAL_MINUTE_MICROSECOND = "MINUTE_MICROSECOND" 804 // INTERVAL_MINUTE_SECOND is the time unit MINUTE_SECOND. 805 INTERVAL_MINUTE_SECOND = "MINUTE_SECOND" 806 // INTERVAL_HOUR_MICROSECOND is the time unit HOUR_MICROSECOND. 807 INTERVAL_HOUR_MICROSECOND = "HOUR_MICROSECOND" 808 // INTERVAL_HOUR_SECOND is the time unit HOUR_SECOND. 809 INTERVAL_HOUR_SECOND = "HOUR_SECOND" 810 // INTERVAL_HOUR_MINUTE is the time unit HOUR_MINUTE. 811 INTERVAL_HOUR_MINUTE = "HOUR_MINUTE" 812 // INTERVAL_DAY_MICROSECOND is the time unit DAY_MICROSECOND. 813 INTERVAL_DAY_MICROSECOND = "DAY_MICROSECOND" 814 // INTERVAL_DAY_SECOND is the time unit DAY_SECOND. 815 INTERVAL_DAY_SECOND = "DAY_SECOND" 816 // INTERVAL_DAY_MINUTE is the time unit DAY_MINUTE. 817 INTERVAL_DAY_MINUTE = "DAY_MINUTE" 818 // INTERVAL_DAY_HOUR is the time unit DAY_HOUR. 819 INTERVAL_DAY_HOUR = "DAY_HOUR" 820 // INTERVAL_YEAR_MONTH is the time unit YEAR_MONTH. 821 INTERVAL_YEAR_MONTH = "YEAR_MONTH" 822 ) 823 824 // onlyHasHiddenPrimaryKey checks the primary key is hidden or not 825 func onlyHasHiddenPrimaryKey(tableDef *TableDef) bool { 826 if tableDef == nil { 827 return false 828 } 829 pk := tableDef.GetPkey() 830 return pk != nil && pk.GetPkeyColName() == catalog.FakePrimaryKeyColName 831 } 832 833 // checkPartitionByRange Check the validity of each partition definition in range partition. 834 func checkPartitionByRange(partitionBinder *PartitionBinder, partitionDef *plan.PartitionByDef, tbInfo *TableDef) error { 835 if partitionDef.PartitionColumns != nil { 836 return checkRangeColumnsPartitionValue(partitionBinder, partitionDef, tbInfo) 837 } 838 return checkRangePartitionValue(partitionBinder, partitionDef, tbInfo) 839 } 840 841 // checkRangePartitionValue check if the 'less than value' for each partition is strictly monotonically increasing. 842 func checkRangePartitionValue(partitionBinder *PartitionBinder, partitionDef *plan.PartitionByDef, tbInfo *TableDef) error { 843 ctx := partitionBinder.GetContext() 844 845 partdefs := partitionDef.Partitions 846 if len(partdefs) == 0 { 847 return nil 848 } 849 850 if _, ok := partdefs[len(partdefs)-1].LessThan[0].Expr.(*plan.Expr_Max); ok { 851 partdefs = partdefs[:len(partdefs)-1] 852 } 853 isUnsigned := types.T(partitionDef.PartitionExpr.Expr.Typ.Id).IsUnsignedInt() 854 var prevRangeValue interface{} 855 for i := 0; i < len(partdefs); i++ { 856 if _, isMaxVal := partdefs[i].LessThan[0].Expr.(*plan.Expr_Max); isMaxVal { 857 return moerr.NewErrPartitionMaxvalue(ctx) 858 } 859 currentRangeValue, err := getRangeValue(ctx, partitionDef, partdefs[i].LessThan[0], isUnsigned, partitionBinder.builder.compCtx.GetProcess()) 860 if err != nil { 861 return err 862 } 863 864 if i == 0 { 865 prevRangeValue = currentRangeValue 866 continue 867 } 868 869 if isUnsigned { 870 if currentRangeValue.(uint64) <= prevRangeValue.(uint64) { 871 return moerr.NewErrRangeNotIncreasing(ctx) 872 } 873 } else { 874 if currentRangeValue.(int64) <= prevRangeValue.(int64) { 875 return moerr.NewErrRangeNotIncreasing(ctx) 876 } 877 } 878 prevRangeValue = currentRangeValue 879 } 880 return nil 881 } 882 883 // checkRangeColumnsPartitionValue check whether "less than value" of each partition is strictly increased in Lexicographic order order. 884 func checkRangeColumnsPartitionValue(partitionBinder *PartitionBinder, partitionDef *plan.PartitionByDef, tbInfo *TableDef) error { 885 ctx := partitionBinder.GetContext() 886 partdefs := partitionDef.Partitions 887 if len(partdefs) < 1 { 888 return moerr.NewPartitionsMustBeDefined(ctx, "RANGE") 889 } 890 891 curr := partdefs[0] 892 if len(curr.LessThan) != len(partitionDef.PartitionColumns.PartitionColumns) { 893 return moerr.NewErrPartitionColumnList(ctx) 894 } 895 896 var prev *plan.PartitionItem 897 for i := 1; i < len(partdefs); i++ { 898 prev, curr = curr, partdefs[i] 899 res, err := compareTwoRangeColumns(ctx, curr, prev, partitionDef, tbInfo, partitionBinder) 900 if err != nil { 901 return err 902 } 903 if !res { 904 return moerr.NewErrRangeNotIncreasing(ctx) 905 } 906 } 907 return nil 908 } 909 910 // getRangeValue gets an integer value from range value expression 911 // The second returned boolean value indicates whether the input string is a constant expression. 912 func getRangeValue(ctx context.Context, partitionDef *plan.PartitionByDef, expr *Expr, unsigned bool, process *process.Process) (interface{}, error) { 913 // Unsigned integer was converted to uint64 914 if unsigned { 915 if cExpr, ok := expr.Expr.(*plan.Expr_Lit); ok { 916 return getIntConstVal[uint64](cExpr), nil 917 } 918 evalExpr, err := EvalPlanExpr(ctx, expr, process) 919 if err != nil { 920 return 0, err 921 } 922 cVal, ok := evalExpr.Expr.(*plan.Expr_Lit) 923 if ok { 924 return getIntConstVal[uint64](cVal), nil 925 } 926 } else { 927 // signed integer was converted to int64 928 if cExpr, ok := expr.Expr.(*plan.Expr_Lit); ok { 929 return getIntConstVal[int64](cExpr), nil 930 } 931 932 // The range partition `less than value` may be not an integer, it could be a constant expression. 933 evalExpr, err := EvalPlanExpr(ctx, expr, process) 934 if err != nil { 935 return 0, err 936 } 937 cVal, ok := evalExpr.Expr.(*plan.Expr_Lit) 938 if ok { 939 return getIntConstVal[int64](cVal), nil 940 } 941 } 942 return 0, moerr.NewFieldTypeNotAllowedAsPartitionField(ctx, partitionDef.PartitionExpr.ExprStr) 943 } 944 945 // compareTwoRangeColumns Check whether the two range columns partition definition values increase in Lexicographic order order 946 func compareTwoRangeColumns(ctx context.Context, curr, prev *plan.PartitionItem, partitionDef *plan.PartitionByDef, tbldef *TableDef, binder *PartitionBinder) (bool, error) { 947 if len(curr.LessThan) != len(partitionDef.PartitionColumns.PartitionColumns) { 948 return false, moerr.NewErrPartitionColumnList(ctx) 949 } 950 951 for i := 0; i < len(partitionDef.PartitionColumns.Columns); i++ { 952 // handling `MAXVALUE` in partition less than value 953 _, ok1 := curr.LessThan[i].Expr.(*plan.Expr_Max) 954 _, ok2 := prev.LessThan[i].Expr.(*plan.Expr_Max) 955 if ok1 && !ok2 { 956 // If current is maxvalue, then it must be greater than previous, so previous cannot be maxvalue 957 return true, nil 958 } 959 960 if ok2 { 961 // Current is not maxvalue, and the previous cannot be maxvalue 962 return false, nil 963 } 964 965 // The range columns tuples values are strictly increasing in dictionary order 966 colInfo := findColumnByName(partitionDef.PartitionColumns.PartitionColumns[i], tbldef) 967 res, err := evalPartitionBoolExpr(ctx, curr.LessThan[i], prev.LessThan[i], colInfo, binder) 968 if err != nil { 969 return false, err 970 } 971 972 if res { 973 return true, nil 974 } 975 } 976 return false, nil 977 } 978 979 // evalPartitionBoolExpr Calculate the bool result of comparing the values of two range partition `less than value` 980 func evalPartitionBoolExpr(ctx context.Context, lOriExpr *Expr, rOriExpr *Expr, colInfo *plan.ColDef, binder *PartitionBinder) (bool, error) { 981 lexpr, err := makePlan2CastExpr(ctx, lOriExpr, colInfo.Typ) 982 if err != nil { 983 return false, err 984 } 985 986 rexpr, err := makePlan2CastExpr(ctx, rOriExpr, colInfo.Typ) 987 if err != nil { 988 return false, err 989 } 990 991 retExpr, err := BindFuncExprImplByPlanExpr(ctx, ">", []*Expr{lexpr, rexpr}) 992 if err != nil { 993 return false, err 994 } 995 996 vec, err := colexec.EvalExpressionOnce(binder.builder.compCtx.GetProcess(), retExpr, []*batch.Batch{batch.EmptyForConstFoldBatch, batch.EmptyForConstFoldBatch}) 997 if err != nil { 998 return false, err 999 } 1000 fixedCol := vector.MustFixedCol[bool](vec) 1001 return fixedCol[0], nil 1002 } 1003 1004 // getIntConstVal Get an integer constant value, the `cExpr` must be integral constant expression 1005 func getIntConstVal[T uint64 | int64](cExpr *plan.Expr_Lit) T { 1006 switch value := cExpr.Lit.Value.(type) { 1007 case *plan.Literal_U8Val: 1008 return T(value.U8Val) 1009 case *plan.Literal_U16Val: 1010 return T(value.U16Val) 1011 case *plan.Literal_U32Val: 1012 return T(value.U32Val) 1013 case *plan.Literal_U64Val: 1014 return T(value.U64Val) 1015 case *plan.Literal_I8Val: 1016 return T(value.I8Val) 1017 case *plan.Literal_I16Val: 1018 return T(value.I16Val) 1019 case *plan.Literal_I32Val: 1020 return T(value.I32Val) 1021 case *plan.Literal_I64Val: 1022 return T(value.I64Val) 1023 default: 1024 panic("the `expr` must be integral constant expression") 1025 } 1026 } 1027 1028 // deepCopyTableCols Deeply copy table's column definition information 1029 // Cols: Table column definitions 1030 // ignoreRowId: Whether to ignore the rowId column 1031 func deepCopyTableCols(Cols []*ColDef, ignoreRowId bool) []*ColDef { 1032 if Cols == nil { 1033 return nil 1034 } 1035 newCols := make([]*plan.ColDef, 0) 1036 for _, col := range Cols { 1037 if ignoreRowId && col.Name == catalog.Row_ID { 1038 continue 1039 } 1040 newCols = append(newCols, DeepCopyColDef(col)) 1041 } 1042 return newCols 1043 }