github.com/whtcorpsinc/milevadb-prod@v0.0.0-20211104133533-f57f4be3b597/dbs/partition.go (about) 1 // Copyright 2020 WHTCORPS INC, Inc. 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 // See the License for the specific language governing permissions and 12 // limitations under the License. 13 14 package dbs 15 16 import ( 17 "bytes" 18 "context" 19 "fmt" 20 "strconv" 21 "strings" 22 "time" 23 24 "github.com/cznic/mathutil" 25 "github.com/whtcorpsinc/BerolinaSQL" 26 "github.com/whtcorpsinc/BerolinaSQL/allegrosql" 27 "github.com/whtcorpsinc/BerolinaSQL/ast" 28 "github.com/whtcorpsinc/BerolinaSQL/format" 29 "github.com/whtcorpsinc/BerolinaSQL/opcode" 30 "github.com/whtcorpsinc/BerolinaSQL/perceptron" 31 "github.com/whtcorpsinc/ekvproto/pkg/spacetimepb" 32 "github.com/whtcorpsinc/errors" 33 "github.com/whtcorpsinc/failpoint" 34 "github.com/whtcorpsinc/milevadb/blockcodec" 35 "github.com/whtcorpsinc/milevadb/causet" 36 "github.com/whtcorpsinc/milevadb/causetstore/einsteindb" 37 "github.com/whtcorpsinc/milevadb/dbs/memristed" 38 "github.com/whtcorpsinc/milevadb/dbs/soliton" 39 "github.com/whtcorpsinc/milevadb/memex" 40 "github.com/whtcorpsinc/milevadb/petri/infosync" 41 "github.com/whtcorpsinc/milevadb/schemareplicant" 42 "github.com/whtcorpsinc/milevadb/soliton/chunk" 43 "github.com/whtcorpsinc/milevadb/soliton/logutil" 44 "github.com/whtcorpsinc/milevadb/soliton/sqlexec" 45 "github.com/whtcorpsinc/milevadb/spacetime" 46 "github.com/whtcorpsinc/milevadb/stochastikctx" 47 "github.com/whtcorpsinc/milevadb/types" 48 "go.uber.org/zap" 49 ) 50 51 const ( 52 partitionMaxValue = "MAXVALUE" 53 ) 54 55 func checkAddPartition(t *spacetime.Meta, job *perceptron.Job) (*perceptron.BlockInfo, *perceptron.PartitionInfo, []perceptron.PartitionDefinition, error) { 56 schemaID := job.SchemaID 57 tblInfo, err := getBlockInfoAndCancelFaultJob(t, job, schemaID) 58 if err != nil { 59 return nil, nil, nil, errors.Trace(err) 60 } 61 partInfo := &perceptron.PartitionInfo{} 62 err = job.DecodeArgs(&partInfo) 63 if err != nil { 64 job.State = perceptron.JobStateCancelled 65 return nil, nil, nil, errors.Trace(err) 66 } 67 if len(tblInfo.Partition.AddingDefinitions) > 0 { 68 return tblInfo, partInfo, tblInfo.Partition.AddingDefinitions, nil 69 } 70 return tblInfo, partInfo, []perceptron.PartitionDefinition{}, nil 71 } 72 73 func onAddBlockPartition(d *dbsCtx, t *spacetime.Meta, job *perceptron.Job) (ver int64, _ error) { 74 // Handle the rolling back job 75 if job.IsRollingback() { 76 ver, err := onDropBlockPartition(t, job) 77 if err != nil { 78 return ver, errors.Trace(err) 79 } 80 return ver, nil 81 } 82 83 tblInfo, partInfo, addingDefinitions, err := checkAddPartition(t, job) 84 if err != nil { 85 return ver, err 86 } 87 88 // In order to skip maintaining the state check in partitionDefinition, MilevaDB use addingDefinition instead of state field. 89 // So here using `job.SchemaState` to judge what the stage of this job is. 90 switch job.SchemaState { 91 case perceptron.StateNone: 92 // job.SchemaState == perceptron.StateNone means the job is in the initial state of add partition. 93 // Here should use partInfo from job directly and do some check action. 94 err = checkAddPartitionTooManyPartitions(uint64(len(tblInfo.Partition.Definitions) + len(partInfo.Definitions))) 95 if err != nil { 96 job.State = perceptron.JobStateCancelled 97 return ver, errors.Trace(err) 98 } 99 100 err = checkAddPartitionValue(tblInfo, partInfo) 101 if err != nil { 102 job.State = perceptron.JobStateCancelled 103 return ver, errors.Trace(err) 104 } 105 106 err = checkAddPartitionNameUnique(tblInfo, partInfo) 107 if err != nil { 108 job.State = perceptron.JobStateCancelled 109 return ver, errors.Trace(err) 110 } 111 // none -> replica only 112 job.SchemaState = perceptron.StateReplicaOnly 113 // move the adding definition into blockInfo. 114 uFIDelateAddingPartitionInfo(partInfo, tblInfo) 115 ver, err = uFIDelateVersionAndBlockInfoWithCheck(t, job, tblInfo, true) 116 case perceptron.StateReplicaOnly: 117 // replica only -> public 118 // Here need do some tiflash replica complement check. 119 // TODO: If a causet is with no TiFlashReplica or it is not available, the replica-only state can be eliminated. 120 if tblInfo.TiFlashReplica != nil && tblInfo.TiFlashReplica.Available { 121 // For available state, the new added partition should wait it's replica to 122 // be finished. Otherwise the query to this partition will be blocked. 123 needWait, err := checkPartitionReplica(addingDefinitions, d) 124 if err != nil { 125 ver, err = convertAddBlockPartitionJob2RollbackJob(t, job, err, tblInfo) 126 return ver, err 127 } 128 if needWait { 129 // The new added partition hasn't been replicated. 130 // Do nothing to the job this time, wait next worker round. 131 time.Sleep(tiflashCheckMilevaDBHTTPAPIHalfInterval) 132 return ver, nil 133 } 134 } 135 136 // For normal and replica finished causet, move the `addingDefinitions` into `Definitions`. 137 uFIDelatePartitionInfo(tblInfo) 138 139 ver, err = uFIDelateVersionAndBlockInfo(t, job, tblInfo, true) 140 if err != nil { 141 return ver, errors.Trace(err) 142 } 143 // Finish this job. 144 job.FinishBlockJob(perceptron.JobStateDone, perceptron.StatePublic, ver, tblInfo) 145 asyncNotifyEvent(d, &soliton.Event{Tp: perceptron.CausetActionAddBlockPartition, BlockInfo: tblInfo, PartInfo: partInfo}) 146 default: 147 err = ErrInvalidDBSState.GenWithStackByArgs("partition", job.SchemaState) 148 } 149 150 return ver, errors.Trace(err) 151 } 152 153 // uFIDelatePartitionInfo merge `addingDefinitions` into `Definitions` in the blockInfo. 154 func uFIDelatePartitionInfo(tblInfo *perceptron.BlockInfo) { 155 parInfo := &perceptron.PartitionInfo{} 156 oldDefs, newDefs := tblInfo.Partition.Definitions, tblInfo.Partition.AddingDefinitions 157 parInfo.Definitions = make([]perceptron.PartitionDefinition, 0, len(newDefs)+len(oldDefs)) 158 parInfo.Definitions = append(parInfo.Definitions, oldDefs...) 159 parInfo.Definitions = append(parInfo.Definitions, newDefs...) 160 tblInfo.Partition.Definitions = parInfo.Definitions 161 tblInfo.Partition.AddingDefinitions = nil 162 } 163 164 // uFIDelateAddingPartitionInfo write adding partitions into `addingDefinitions` field in the blockInfo. 165 func uFIDelateAddingPartitionInfo(partitionInfo *perceptron.PartitionInfo, tblInfo *perceptron.BlockInfo) { 166 newDefs := partitionInfo.Definitions 167 tblInfo.Partition.AddingDefinitions = make([]perceptron.PartitionDefinition, 0, len(newDefs)) 168 tblInfo.Partition.AddingDefinitions = append(tblInfo.Partition.AddingDefinitions, newDefs...) 169 } 170 171 // rollbackAddingPartitionInfo remove the `addingDefinitions` in the blockInfo. 172 func rollbackAddingPartitionInfo(tblInfo *perceptron.BlockInfo) []int64 { 173 physicalBlockIDs := make([]int64, 0, len(tblInfo.Partition.AddingDefinitions)) 174 for _, one := range tblInfo.Partition.AddingDefinitions { 175 physicalBlockIDs = append(physicalBlockIDs, one.ID) 176 } 177 tblInfo.Partition.AddingDefinitions = nil 178 return physicalBlockIDs 179 } 180 181 // checkAddPartitionValue values less than value must be strictly increasing for each partition. 182 func checkAddPartitionValue(spacetime *perceptron.BlockInfo, part *perceptron.PartitionInfo) error { 183 if spacetime.Partition.Type == perceptron.PartitionTypeRange && len(spacetime.Partition.DeferredCausets) == 0 { 184 newDefs, oldDefs := part.Definitions, spacetime.Partition.Definitions 185 rangeValue := oldDefs[len(oldDefs)-1].LessThan[0] 186 if strings.EqualFold(rangeValue, "MAXVALUE") { 187 return errors.Trace(ErrPartitionMaxvalue) 188 } 189 190 currentRangeValue, err := strconv.Atoi(rangeValue) 191 if err != nil { 192 return errors.Trace(err) 193 } 194 195 for i := 0; i < len(newDefs); i++ { 196 ifMaxvalue := strings.EqualFold(newDefs[i].LessThan[0], "MAXVALUE") 197 if ifMaxvalue && i == len(newDefs)-1 { 198 return nil 199 } else if ifMaxvalue && i != len(newDefs)-1 { 200 return errors.Trace(ErrPartitionMaxvalue) 201 } 202 203 nextRangeValue, err := strconv.Atoi(newDefs[i].LessThan[0]) 204 if err != nil { 205 return errors.Trace(err) 206 } 207 if nextRangeValue <= currentRangeValue { 208 return errors.Trace(ErrRangeNotIncreasing) 209 } 210 currentRangeValue = nextRangeValue 211 } 212 } 213 return nil 214 } 215 216 func checkPartitionReplica(addingDefinitions []perceptron.PartitionDefinition, d *dbsCtx) (needWait bool, err error) { 217 ctx := context.Background() 218 FIDelCli := d.causetstore.(einsteindb.CausetStorage).GetRegionCache().FIDelClient() 219 stores, err := FIDelCli.GetAllStores(ctx) 220 if err != nil { 221 return needWait, errors.Trace(err) 222 } 223 for _, fidel := range addingDefinitions { 224 startKey, endKey := blockcodec.GetBlockHandleKeyRange(fidel.ID) 225 regions, err := FIDelCli.ScanRegions(ctx, startKey, endKey, -1) 226 if err != nil { 227 return needWait, errors.Trace(err) 228 } 229 // For every region in the partition, if it has some corresponding peers and 230 // no pending peers, that means the replication has completed. 231 for _, region := range regions { 232 regionState, err := FIDelCli.GetRegionByID(ctx, region.Meta.Id) 233 if err != nil { 234 return needWait, errors.Trace(err) 235 } 236 tiflashPeerAtLeastOne := checkTiFlashPeerStoreAtLeastOne(stores, regionState.Meta.Peers) 237 // It's unnecessary to wait all tiflash peer to be replicated. 238 // Here only make sure that tiflash peer count > 0 (at least one). 239 if tiflashPeerAtLeastOne { 240 continue 241 } 242 needWait = true 243 logutil.BgLogger().Info("[dbs] partition replicas check failed in replica-only DBS state", zap.Int64("pID", fidel.ID), zap.Uint64("wait region ID", region.Meta.Id), zap.Bool("tiflash peer at least one", tiflashPeerAtLeastOne), zap.Time("check time", time.Now())) 244 return needWait, nil 245 } 246 } 247 logutil.BgLogger().Info("[dbs] partition replicas check ok in replica-only DBS state") 248 return needWait, nil 249 } 250 251 func checkTiFlashPeerStoreAtLeastOne(stores []*spacetimepb.CausetStore, peers []*spacetimepb.Peer) bool { 252 for _, peer := range peers { 253 for _, causetstore := range stores { 254 if peer.StoreId == causetstore.Id && storeHasEngineTiFlashLabel(causetstore) { 255 return true 256 } 257 } 258 } 259 return false 260 } 261 262 func storeHasEngineTiFlashLabel(causetstore *spacetimepb.CausetStore) bool { 263 for _, label := range causetstore.Labels { 264 if label.Key == "engine" && label.Value == "tiflash" { 265 return true 266 } 267 } 268 return false 269 } 270 271 // buildBlockPartitionInfo builds partition info and checks for some errors. 272 func buildBlockPartitionInfo(ctx stochastikctx.Context, s *ast.CreateBlockStmt) (*perceptron.PartitionInfo, error) { 273 if s.Partition == nil { 274 return nil, nil 275 } 276 277 if ctx.GetStochastikVars().EnableBlockPartition == "off" { 278 ctx.GetStochastikVars().StmtCtx.AppendWarning(errBlockPartitionDisabled) 279 return nil, nil 280 } 281 282 var enable bool 283 // When milevadb_enable_block_partition is 'on' or 'auto'. 284 if s.Partition.Tp == perceptron.PartitionTypeRange { 285 if s.Partition.Sub == nil { 286 // Partition by range memex is enabled by default. 287 if s.Partition.DeferredCausetNames == nil { 288 enable = true 289 } 290 // Partition by range columns and just one column. 291 if len(s.Partition.DeferredCausetNames) == 1 { 292 enable = true 293 } 294 } 295 } 296 // Partition by hash is enabled by default. 297 // Note that linear hash is not enabled. 298 if s.Partition.Tp == perceptron.PartitionTypeHash { 299 if !s.Partition.Linear && s.Partition.Sub == nil { 300 enable = true 301 } 302 } 303 304 if !enable { 305 ctx.GetStochastikVars().StmtCtx.AppendWarning(errUnsupportedCreatePartition) 306 return nil, nil 307 } 308 309 pi := &perceptron.PartitionInfo{ 310 Type: s.Partition.Tp, 311 Enable: enable, 312 Num: s.Partition.Num, 313 } 314 if s.Partition.Expr != nil { 315 buf := new(bytes.Buffer) 316 restoreCtx := format.NewRestoreCtx(format.DefaultRestoreFlags, buf) 317 if err := s.Partition.Expr.Restore(restoreCtx); err != nil { 318 return nil, err 319 } 320 pi.Expr = buf.String() 321 } else if s.Partition.DeferredCausetNames != nil { 322 // TODO: Support multiple columns for 'PARTITION BY RANGE COLUMNS'. 323 if len(s.Partition.DeferredCausetNames) != 1 { 324 pi.Enable = false 325 ctx.GetStochastikVars().StmtCtx.AppendWarning(ErrUnsupportedPartitionByRangeDeferredCausets) 326 } 327 pi.DeferredCausets = make([]perceptron.CIStr, 0, len(s.Partition.DeferredCausetNames)) 328 for _, cn := range s.Partition.DeferredCausetNames { 329 pi.DeferredCausets = append(pi.DeferredCausets, cn.Name) 330 } 331 } 332 333 if s.Partition.Tp == perceptron.PartitionTypeRange { 334 if err := buildRangePartitionDefinitions(ctx, s, pi); err != nil { 335 return nil, errors.Trace(err) 336 } 337 } else if s.Partition.Tp == perceptron.PartitionTypeHash { 338 if err := buildHashPartitionDefinitions(ctx, s, pi); err != nil { 339 return nil, errors.Trace(err) 340 } 341 } 342 return pi, nil 343 } 344 345 func buildHashPartitionDefinitions(ctx stochastikctx.Context, s *ast.CreateBlockStmt, pi *perceptron.PartitionInfo) error { 346 if err := checkAddPartitionTooManyPartitions(pi.Num); err != nil { 347 return err 348 } 349 350 defs := make([]perceptron.PartitionDefinition, pi.Num) 351 for i := 0; i < len(defs); i++ { 352 if len(s.Partition.Definitions) == 0 { 353 defs[i].Name = perceptron.NewCIStr(fmt.Sprintf("p%v", i)) 354 } else { 355 def := s.Partition.Definitions[i] 356 defs[i].Name = def.Name 357 defs[i].Comment, _ = def.Comment() 358 } 359 } 360 pi.Definitions = defs 361 return nil 362 } 363 364 func buildRangePartitionDefinitions(ctx stochastikctx.Context, s *ast.CreateBlockStmt, pi *perceptron.PartitionInfo) (err error) { 365 for _, def := range s.Partition.Definitions { 366 comment, _ := def.Comment() 367 err = checkTooLongBlock(def.Name) 368 if err != nil { 369 return err 370 } 371 piDef := perceptron.PartitionDefinition{ 372 Name: def.Name, 373 Comment: comment, 374 } 375 376 buf := new(bytes.Buffer) 377 // Range columns partitions support multi-column partitions. 378 for _, expr := range def.Clause.(*ast.PartitionDefinitionClauseLessThan).Exprs { 379 expr.Format(buf) 380 piDef.LessThan = append(piDef.LessThan, buf.String()) 381 buf.Reset() 382 } 383 pi.Definitions = append(pi.Definitions, piDef) 384 } 385 return nil 386 } 387 388 func checkPartitionNameUnique(pi *perceptron.PartitionInfo) error { 389 newPars := pi.Definitions 390 partNames := make(map[string]struct{}, len(newPars)) 391 for _, newPar := range newPars { 392 if _, ok := partNames[newPar.Name.L]; ok { 393 return ErrSameNamePartition.GenWithStackByArgs(newPar.Name) 394 } 395 partNames[newPar.Name.L] = struct{}{} 396 } 397 return nil 398 } 399 400 func checkAddPartitionNameUnique(tbInfo *perceptron.BlockInfo, pi *perceptron.PartitionInfo) error { 401 partNames := make(map[string]struct{}) 402 if tbInfo.Partition != nil { 403 oldPars := tbInfo.Partition.Definitions 404 for _, oldPar := range oldPars { 405 partNames[oldPar.Name.L] = struct{}{} 406 } 407 } 408 newPars := pi.Definitions 409 for _, newPar := range newPars { 410 if _, ok := partNames[newPar.Name.L]; ok { 411 return ErrSameNamePartition.GenWithStackByArgs(newPar.Name) 412 } 413 partNames[newPar.Name.L] = struct{}{} 414 } 415 return nil 416 } 417 418 func checkAndOverridePartitionID(newBlockInfo, oldBlockInfo *perceptron.BlockInfo) error { 419 // If any old partitionInfo has lost, that means the partition ID lost too, so did the data, repair failed. 420 if newBlockInfo.Partition == nil { 421 return nil 422 } 423 if oldBlockInfo.Partition == nil { 424 return ErrRepairBlockFail.GenWithStackByArgs("Old causet doesn't have partitions") 425 } 426 if newBlockInfo.Partition.Type != oldBlockInfo.Partition.Type { 427 return ErrRepairBlockFail.GenWithStackByArgs("Partition type should be the same") 428 } 429 // Check whether partitionType is hash partition. 430 if newBlockInfo.Partition.Type == perceptron.PartitionTypeHash { 431 if newBlockInfo.Partition.Num != oldBlockInfo.Partition.Num { 432 return ErrRepairBlockFail.GenWithStackByArgs("Hash partition num should be the same") 433 } 434 } 435 for i, newOne := range newBlockInfo.Partition.Definitions { 436 found := false 437 for _, oldOne := range oldBlockInfo.Partition.Definitions { 438 // Fix issue 17952 which wanna substitute partition range expr. 439 // So eliminate stringSliceEqual(newOne.LessThan, oldOne.LessThan) here. 440 if newOne.Name.L == oldOne.Name.L { 441 newBlockInfo.Partition.Definitions[i].ID = oldOne.ID 442 found = true 443 break 444 } 445 } 446 if !found { 447 return ErrRepairBlockFail.GenWithStackByArgs("Partition " + newOne.Name.L + " has lost") 448 } 449 } 450 return nil 451 } 452 453 func stringSliceEqual(a, b []string) bool { 454 if len(a) != len(b) { 455 return false 456 } 457 if len(a) == 0 { 458 return true 459 } 460 // Accelerate the compare by eliminate index bound check. 461 b = b[:len(a)] 462 for i, v := range a { 463 if v != b[i] { 464 return false 465 } 466 } 467 return true 468 } 469 470 // hasTimestampField derives from https://github.com/allegrosql/allegrosql-server/blob/5.7/allegrosql/item_func.h#L387 471 func hasTimestampField(ctx stochastikctx.Context, tblInfo *perceptron.BlockInfo, expr ast.ExprNode) (bool, error) { 472 partDefCauss, err := checkPartitionDeferredCausets(tblInfo, expr) 473 if err != nil { 474 return false, err 475 } 476 477 for _, c := range partDefCauss { 478 if c.FieldType.Tp == allegrosql.TypeTimestamp { 479 return true, nil 480 } 481 } 482 483 return false, nil 484 } 485 486 // hasDateField derives from https://github.com/allegrosql/allegrosql-server/blob/5.7/allegrosql/item_func.h#L399 487 func hasDateField(ctx stochastikctx.Context, tblInfo *perceptron.BlockInfo, expr ast.ExprNode) (bool, error) { 488 partDefCauss, err := checkPartitionDeferredCausets(tblInfo, expr) 489 if err != nil { 490 return false, err 491 } 492 493 for _, c := range partDefCauss { 494 if c.FieldType.Tp == allegrosql.TypeDate || c.FieldType.Tp == allegrosql.TypeDatetime { 495 return true, nil 496 } 497 } 498 499 return false, nil 500 } 501 502 // hasTimeField derives from https://github.com/allegrosql/allegrosql-server/blob/5.7/allegrosql/item_func.h#L412 503 func hasTimeField(ctx stochastikctx.Context, tblInfo *perceptron.BlockInfo, expr ast.ExprNode) (bool, error) { 504 partDefCauss, err := checkPartitionDeferredCausets(tblInfo, expr) 505 if err != nil { 506 return false, err 507 } 508 509 for _, c := range partDefCauss { 510 if c.FieldType.Tp == allegrosql.TypeDatetime || c.FieldType.Tp == allegrosql.TypeDuration { 511 return true, nil 512 } 513 } 514 515 return false, nil 516 } 517 518 // defaultTimezoneDependent derives from https://github.com/allegrosql/allegrosql-server/blob/5.7/allegrosql/item_func.h#L445 519 // We assume the result of any function that has a TIMESTAMP argument to be 520 // timezone-dependent, since a TIMESTAMP value in both numeric and string 521 // contexts is interpreted according to the current timezone. 522 // The only exception is UNIX_TIMESTAMP() which returns the internal 523 // representation of a TIMESTAMP argument verbatim, and thus does not depend on 524 // the timezone. 525 func defaultTimezoneDependent(ctx stochastikctx.Context, tblInfo *perceptron.BlockInfo, expr ast.ExprNode) (bool, error) { 526 v, err := hasTimestampField(ctx, tblInfo, expr) 527 if err != nil { 528 return false, err 529 } 530 531 return !v, nil 532 } 533 534 func checkPartitionFuncCallValid(ctx stochastikctx.Context, tblInfo *perceptron.BlockInfo, expr *ast.FuncCallExpr) error { 535 // We assume the result of any function that has a TIMESTAMP argument to be 536 // timezone-dependent, since a TIMESTAMP value in both numeric and string 537 // contexts is interpreted according to the current timezone. 538 // The only exception is UNIX_TIMESTAMP() which returns the internal 539 // representation of a TIMESTAMP argument verbatim, and thus does not depend on 540 // the timezone. 541 // See https://github.com/allegrosql/allegrosql-server/blob/5.7/allegrosql/item_func.h#L445 542 if expr.FnName.L != ast.UnixTimestamp { 543 for _, arg := range expr.Args { 544 if colName, ok := arg.(*ast.DeferredCausetNameExpr); ok { 545 col := findDeferredCausetByName(colName.Name.Name.L, tblInfo) 546 if col == nil { 547 return ErrBadField.GenWithStackByArgs(colName.Name.Name.O, "memex") 548 } 549 550 if ok && col.FieldType.Tp == allegrosql.TypeTimestamp { 551 return errors.Trace(errWrongExprInPartitionFunc) 552 } 553 } 554 } 555 } 556 557 // check function which allowed in partitioning memexs 558 // see https://dev.allegrosql.com/doc/allegrosql-partitioning-excerpt/5.7/en/partitioning-limitations-functions.html 559 switch expr.FnName.L { 560 // Mysql don't allow creating partitions with memexs with non matching 561 // arguments as a (sub)partitioning function, 562 // but we want to allow such memexs when opening existing blocks for 563 // easier maintenance. This exception should be deprecated at some point in future so that we always throw an error. 564 // See https://github.com/allegrosql/allegrosql-server/blob/5.7/allegrosql/sql_partition.cc#L1072 565 case ast.Day, ast.DayOfMonth, ast.DayOfWeek, ast.DayOfYear, ast.Month, ast.Quarter, ast.ToDays, ast.ToSeconds, 566 ast.Weekday, ast.Year, ast.YearWeek: 567 return checkResultOK(hasDateField(ctx, tblInfo, expr)) 568 case ast.Hour, ast.MicroSecond, ast.Minute, ast.Second, ast.TimeToSec: 569 return checkResultOK(hasTimeField(ctx, tblInfo, expr)) 570 case ast.UnixTimestamp: 571 if len(expr.Args) != 1 { 572 return errors.Trace(errWrongExprInPartitionFunc) 573 } 574 col, err := memex.RewriteSimpleExprWithBlockInfo(ctx, tblInfo, expr.Args[0]) 575 if err != nil { 576 return errors.Trace(err) 577 } 578 if col.GetType().Tp != allegrosql.TypeTimestamp { 579 return errors.Trace(errWrongExprInPartitionFunc) 580 } 581 return nil 582 case ast.Abs, ast.Ceiling, ast.DateDiff, ast.Extract, ast.Floor, ast.Mod: 583 for _, arg := range expr.Args { 584 if err := checkPartitionExprValid(ctx, tblInfo, arg); err != nil { 585 return err 586 } 587 } 588 return nil 589 } 590 return errors.Trace(ErrPartitionFunctionIsNotAllowed) 591 } 592 593 // checkPartitionExprValid checks partition memex validly. 594 func checkPartitionExprValid(ctx stochastikctx.Context, tblInfo *perceptron.BlockInfo, expr ast.ExprNode) error { 595 switch v := expr.(type) { 596 case *ast.FuncCastExpr, *ast.CaseExpr, *ast.SubqueryExpr, *ast.WindowFuncExpr, *ast.RowExpr, *ast.DefaultExpr, *ast.ValuesExpr: 597 return errors.Trace(ErrPartitionFunctionIsNotAllowed) 598 case *ast.FuncCallExpr: 599 return checkPartitionFuncCallValid(ctx, tblInfo, v) 600 case *ast.BinaryOperationExpr: 601 // The DIV operator (opcode.IntDiv) is also supported; the / operator ( opcode.Div ) is not permitted. 602 // see https://dev.allegrosql.com/doc/refman/5.7/en/partitioning-limitations.html 603 switch v.Op { 604 case opcode.Or, opcode.And, opcode.Xor, opcode.LeftShift, opcode.RightShift, opcode.BitNeg, opcode.Div: 605 return errors.Trace(ErrPartitionFunctionIsNotAllowed) 606 default: 607 if err := checkPartitionExprValid(ctx, tblInfo, v.L); err != nil { 608 return errors.Trace(err) 609 } 610 if err := checkPartitionExprValid(ctx, tblInfo, v.R); err != nil { 611 return errors.Trace(err) 612 } 613 } 614 return nil 615 case *ast.UnaryOperationExpr: 616 if v.Op == opcode.BitNeg { 617 return errors.Trace(ErrPartitionFunctionIsNotAllowed) 618 } 619 if err := checkPartitionExprValid(ctx, tblInfo, v.V); err != nil { 620 return errors.Trace(err) 621 } 622 return nil 623 case *ast.ParenthesesExpr: 624 return checkPartitionExprValid(ctx, tblInfo, v.Expr) 625 } 626 return nil 627 } 628 629 // checkPartitionFuncValid checks partition function validly. 630 func checkPartitionFuncValid(ctx stochastikctx.Context, tblInfo *perceptron.BlockInfo, expr ast.ExprNode) error { 631 err := checkPartitionExprValid(ctx, tblInfo, expr) 632 if err != nil { 633 return err 634 } 635 // check constant. 636 _, err = checkPartitionDeferredCausets(tblInfo, expr) 637 return err 638 } 639 640 // checkResultOK derives from https://github.com/allegrosql/allegrosql-server/blob/5.7/allegrosql/item_timefunc 641 // For partition blocks, allegrosql do not support Constant, random or timezone-dependent memexs 642 // Based on allegrosql code to check whether field is valid, every time related type has check_valid_arguments_processor function. 643 func checkResultOK(ok bool, err error) error { 644 if err != nil { 645 return err 646 } 647 648 if !ok { 649 return errors.Trace(errWrongExprInPartitionFunc) 650 } 651 652 return nil 653 } 654 655 func checkPartitionDeferredCausets(tblInfo *perceptron.BlockInfo, expr ast.ExprNode) ([]*perceptron.DeferredCausetInfo, error) { 656 var buf strings.Builder 657 restoreCtx := format.NewRestoreCtx(format.DefaultRestoreFlags, &buf) 658 err := expr.Restore(restoreCtx) 659 if err != nil { 660 return nil, errors.Trace(err) 661 } 662 partDefCauss, err := extractPartitionDeferredCausets(buf.String(), tblInfo) 663 if err != nil { 664 return nil, err 665 } 666 667 if len(partDefCauss) == 0 { 668 return nil, errors.Trace(errWrongExprInPartitionFunc) 669 } 670 671 return partDefCauss, nil 672 } 673 674 // checkPartitionFuncType checks partition function return type. 675 func checkPartitionFuncType(ctx stochastikctx.Context, s *ast.CreateBlockStmt, tblInfo *perceptron.BlockInfo) error { 676 if s.Partition.Expr == nil { 677 return nil 678 } 679 var buf strings.Builder 680 restoreCtx := format.NewRestoreCtx(format.DefaultRestoreFlags, &buf) 681 if err := s.Partition.Expr.Restore(restoreCtx); err != nil { 682 return errors.Trace(err) 683 } 684 exprStr := buf.String() 685 if s.Partition.Tp == perceptron.PartitionTypeRange || s.Partition.Tp == perceptron.PartitionTypeHash { 686 // if partition by columnExpr, check the column type 687 if _, ok := s.Partition.Expr.(*ast.DeferredCausetNameExpr); ok { 688 for _, col := range tblInfo.DeferredCausets { 689 name := strings.Replace(col.Name.String(), ".", "`.`", -1) 690 // Range partitioning key supported types: tinyint, smallint, mediumint, int and bigint. 691 if !validRangePartitionType(col) && fmt.Sprintf("`%s`", name) == exprStr { 692 return errors.Trace(ErrNotAllowedTypeInPartition.GenWithStackByArgs(exprStr)) 693 } 694 } 695 } 696 } 697 698 e, err := memex.ParseSimpleExprWithBlockInfo(ctx, exprStr, tblInfo) 699 if err != nil { 700 return errors.Trace(err) 701 } 702 if e.GetType().EvalType() == types.ETInt { 703 return nil 704 } 705 if s.Partition.Tp == perceptron.PartitionTypeHash { 706 if _, ok := s.Partition.Expr.(*ast.DeferredCausetNameExpr); ok { 707 return ErrNotAllowedTypeInPartition.GenWithStackByArgs(exprStr) 708 } 709 } 710 711 return ErrPartitionFuncNotAllowed.GenWithStackByArgs("PARTITION") 712 } 713 714 // checkCreatePartitionValue checks whether `less than value` is strictly increasing for each partition. 715 // Side effect: it may simplify the partition range definition from a constant memex to an integer. 716 func checkCreatePartitionValue(ctx stochastikctx.Context, tblInfo *perceptron.BlockInfo) error { 717 pi := tblInfo.Partition 718 defs := pi.Definitions 719 if len(defs) == 0 { 720 return nil 721 } 722 723 defcaus := tblInfo.DeferredCausets 724 if strings.EqualFold(defs[len(defs)-1].LessThan[0], partitionMaxValue) { 725 defs = defs[:len(defs)-1] 726 } 727 isUnsignedBigint := isRangePartitionDefCausUnsignedBigint(defcaus, pi) 728 var prevRangeValue interface{} 729 for i := 0; i < len(defs); i++ { 730 if strings.EqualFold(defs[i].LessThan[0], partitionMaxValue) { 731 return errors.Trace(ErrPartitionMaxvalue) 732 } 733 734 currentRangeValue, fromExpr, err := getRangeValue(ctx, defs[i].LessThan[0], isUnsignedBigint) 735 if err != nil { 736 return errors.Trace(err) 737 } 738 if fromExpr { 739 // Constant fold the memex. 740 defs[i].LessThan[0] = fmt.Sprintf("%d", currentRangeValue) 741 } 742 743 if i == 0 { 744 prevRangeValue = currentRangeValue 745 continue 746 } 747 748 if isUnsignedBigint { 749 if currentRangeValue.(uint64) <= prevRangeValue.(uint64) { 750 return errors.Trace(ErrRangeNotIncreasing) 751 } 752 } else { 753 if currentRangeValue.(int64) <= prevRangeValue.(int64) { 754 return errors.Trace(ErrRangeNotIncreasing) 755 } 756 } 757 prevRangeValue = currentRangeValue 758 } 759 return nil 760 } 761 762 // getRangeValue gets an integer from the range value string. 763 // The returned boolean value indicates whether the input string is a constant memex. 764 func getRangeValue(ctx stochastikctx.Context, str string, unsignedBigint bool) (interface{}, bool, error) { 765 // Unsigned bigint was converted to uint64 handle. 766 if unsignedBigint { 767 if value, err := strconv.ParseUint(str, 10, 64); err == nil { 768 return value, false, nil 769 } 770 771 e, err1 := memex.ParseSimpleExprWithBlockInfo(ctx, str, &perceptron.BlockInfo{}) 772 if err1 != nil { 773 return 0, false, err1 774 } 775 res, isNull, err2 := e.EvalInt(ctx, chunk.Row{}) 776 if err2 == nil && !isNull { 777 return uint64(res), true, nil 778 } 779 } else { 780 if value, err := strconv.ParseInt(str, 10, 64); err == nil { 781 return value, false, nil 782 } 783 // The range value maybe not an integer, it could be a constant memex. 784 // For example, the following two cases are the same: 785 // PARTITION p0 VALUES LESS THAN (TO_SECONDS('2004-01-01')) 786 // PARTITION p0 VALUES LESS THAN (63340531200) 787 e, err1 := memex.ParseSimpleExprWithBlockInfo(ctx, str, &perceptron.BlockInfo{}) 788 if err1 != nil { 789 return 0, false, err1 790 } 791 res, isNull, err2 := e.EvalInt(ctx, chunk.Row{}) 792 if err2 == nil && !isNull { 793 return res, true, nil 794 } 795 } 796 return 0, false, ErrNotAllowedTypeInPartition.GenWithStackByArgs(str) 797 } 798 799 // validRangePartitionType checks the type supported by the range partitioning key. 800 func validRangePartitionType(col *perceptron.DeferredCausetInfo) bool { 801 switch col.FieldType.EvalType() { 802 case types.ETInt: 803 return true 804 default: 805 return false 806 } 807 } 808 809 // checkDropBlockPartition checks if the partition exists and does not allow deleting the last existing partition in the causet. 810 func checkDropBlockPartition(spacetime *perceptron.BlockInfo, partLowerNames []string) error { 811 pi := spacetime.Partition 812 if pi.Type != perceptron.PartitionTypeRange && pi.Type != perceptron.PartitionTypeList { 813 return errOnlyOnRangeListPartition.GenWithStackByArgs("DROP") 814 } 815 oldDefs := pi.Definitions 816 for _, pn := range partLowerNames { 817 found := false 818 for _, def := range oldDefs { 819 if def.Name.L == pn { 820 found = true 821 break 822 } 823 } 824 if !found { 825 return errors.Trace(ErrDropPartitionNonExistent.GenWithStackByArgs(pn)) 826 } 827 } 828 if len(oldDefs) == len(partLowerNames) { 829 return errors.Trace(ErrDropLastPartition) 830 } 831 return nil 832 } 833 834 // removePartitionInfo each dbs job deletes a partition. 835 func removePartitionInfo(tblInfo *perceptron.BlockInfo, partLowerNames []string) []int64 { 836 oldDefs := tblInfo.Partition.Definitions 837 newDefs := make([]perceptron.PartitionDefinition, 0, len(oldDefs)-len(partLowerNames)) 838 pids := make([]int64, 0, len(partLowerNames)) 839 840 // consider using a map to probe partLowerNames if too many partLowerNames 841 for i := range oldDefs { 842 found := false 843 for _, partName := range partLowerNames { 844 if oldDefs[i].Name.L == partName { 845 found = true 846 break 847 } 848 } 849 if found { 850 pids = append(pids, oldDefs[i].ID) 851 } else { 852 newDefs = append(newDefs, oldDefs[i]) 853 } 854 } 855 856 tblInfo.Partition.Definitions = newDefs 857 return pids 858 } 859 860 func getPartitionDef(tblInfo *perceptron.BlockInfo, partName string) (index int, def *perceptron.PartitionDefinition, _ error) { 861 defs := tblInfo.Partition.Definitions 862 for i := 0; i < len(defs); i++ { 863 if strings.EqualFold(defs[i].Name.L, strings.ToLower(partName)) { 864 return i, &(defs[i]), nil 865 } 866 } 867 return index, nil, causet.ErrUnknownPartition.GenWithStackByArgs(partName, tblInfo.Name.O) 868 } 869 870 func buildPlacementDropMemrules(schemaID, blockID int64, partitionIDs []int64) []*memristed.MemruleOp { 871 rules := make([]*memristed.MemruleOp, 0, len(partitionIDs)) 872 for _, partitionID := range partitionIDs { 873 rules = append(rules, &memristed.MemruleOp{ 874 CausetAction: memristed.MemruleOFIDelel, 875 DeleteByIDPrefix: true, 876 Memrule: &memristed.Memrule{ 877 GroupID: memristed.MemruleDefaultGroupID, 878 ID: fmt.Sprintf("%d_t%d_p%d", schemaID, blockID, partitionID), 879 }, 880 }) 881 } 882 return rules 883 } 884 885 // onDropBlockPartition deletes old partition spacetime. 886 func onDropBlockPartition(t *spacetime.Meta, job *perceptron.Job) (ver int64, _ error) { 887 var partNames []string 888 if err := job.DecodeArgs(&partNames); err != nil { 889 job.State = perceptron.JobStateCancelled 890 return ver, errors.Trace(err) 891 } 892 tblInfo, err := getBlockInfoAndCancelFaultJob(t, job, job.SchemaID) 893 if err != nil { 894 return ver, errors.Trace(err) 895 } 896 var physicalBlockIDs []int64 897 if job.Type == perceptron.CausetActionAddBlockPartition { 898 // It is rollbacked from adding causet partition, just remove addingDefinitions from blockInfo. 899 physicalBlockIDs = rollbackAddingPartitionInfo(tblInfo) 900 } else { 901 // If an error occurs, it returns that it cannot delete all partitions or that the partition doesn't exist. 902 err = checkDropBlockPartition(tblInfo, partNames) 903 if err != nil { 904 job.State = perceptron.JobStateCancelled 905 return ver, errors.Trace(err) 906 } 907 physicalBlockIDs = removePartitionInfo(tblInfo, partNames) 908 } 909 910 rules := buildPlacementDropMemrules(job.SchemaID, tblInfo.ID, physicalBlockIDs) 911 err = infosync.UFIDelatePlacementMemrules(nil, rules) 912 if err != nil { 913 job.State = perceptron.JobStateCancelled 914 return ver, errors.Wrapf(err, "failed to notify FIDel the memristed rules") 915 } 916 917 ver, err = uFIDelateVersionAndBlockInfo(t, job, tblInfo, true) 918 if err != nil { 919 return ver, errors.Trace(err) 920 } 921 // Finish this job. 922 if job.IsRollingback() { 923 job.FinishBlockJob(perceptron.JobStateRollbackDone, perceptron.StateNone, ver, tblInfo) 924 } else { 925 job.FinishBlockJob(perceptron.JobStateDone, perceptron.StateNone, ver, tblInfo) 926 } 927 928 // A background job will be created to delete old partition data. 929 job.Args = []interface{}{physicalBlockIDs} 930 return ver, nil 931 } 932 933 func buildPlacementTruncateMemrules(rules []*memristed.MemruleOp, schemaID, blockID, jobID int64, oldIDs []int64, newPartitions []perceptron.PartitionDefinition) []*memristed.MemruleOp { 934 newMemrules := make([]*memristed.MemruleOp, 0, len(oldIDs)) 935 for i, oldID := range oldIDs { 936 prefix := fmt.Sprintf("%d_t%d_p%d", schemaID, blockID, oldID) 937 for _, rule := range rules { 938 if strings.HasPrefix(rule.ID, prefix) { 939 // delete the old rule 940 newMemrules = append(newMemrules, &memristed.MemruleOp{ 941 CausetAction: memristed.MemruleOFIDelel, 942 Memrule: &memristed.Memrule{ 943 GroupID: memristed.MemruleDefaultGroupID, 944 ID: rule.ID, 945 }, 946 }) 947 948 // add the new rule 949 rule.CausetAction = memristed.MemruleOpAdd 950 rule.ID = fmt.Sprintf("%d_t%d_p%d_%s_%d_%d", schemaID, blockID, newPartitions[i].ID, rule.Role, jobID, i) 951 newMemrules = append(newMemrules, rule) 952 break 953 } 954 } 955 } 956 return newMemrules 957 } 958 959 // onTruncateBlockPartition truncates old partition spacetime. 960 func onTruncateBlockPartition(d *dbsCtx, t *spacetime.Meta, job *perceptron.Job) (int64, error) { 961 var ver int64 962 var oldIDs []int64 963 if err := job.DecodeArgs(&oldIDs); err != nil { 964 job.State = perceptron.JobStateCancelled 965 return ver, errors.Trace(err) 966 } 967 tblInfo, err := getBlockInfoAndCancelFaultJob(t, job, job.SchemaID) 968 if err != nil { 969 return ver, errors.Trace(err) 970 } 971 pi := tblInfo.GetPartitionInfo() 972 if pi == nil { 973 return ver, errors.Trace(ErrPartitionMgmtOnNonpartitioned) 974 } 975 976 newPartitions := make([]perceptron.PartitionDefinition, 0, len(oldIDs)) 977 for _, oldID := range oldIDs { 978 for i := 0; i < len(pi.Definitions); i++ { 979 def := &pi.Definitions[i] 980 if def.ID == oldID { 981 pid, err1 := t.GenGlobalID() 982 if err != nil { 983 return ver, errors.Trace(err1) 984 } 985 def.ID = pid 986 // Shallow copy only use the def.ID in event handle. 987 newPartitions = append(newPartitions, *def) 988 break 989 } 990 } 991 } 992 if len(newPartitions) == 0 { 993 return ver, causet.ErrUnknownPartition.GenWithStackByArgs("drop?", tblInfo.Name.O) 994 } 995 996 // Clear the tiflash replica available status. 997 if tblInfo.TiFlashReplica != nil { 998 tblInfo.TiFlashReplica.Available = false 999 // Set partition replica become unavailable. 1000 for _, oldID := range oldIDs { 1001 for i, id := range tblInfo.TiFlashReplica.AvailablePartitionIDs { 1002 if id == oldID { 1003 newIDs := tblInfo.TiFlashReplica.AvailablePartitionIDs[:i] 1004 newIDs = append(newIDs, tblInfo.TiFlashReplica.AvailablePartitionIDs[i+1:]...) 1005 tblInfo.TiFlashReplica.AvailablePartitionIDs = newIDs 1006 break 1007 } 1008 } 1009 } 1010 } 1011 1012 var rules []*memristed.MemruleOp 1013 1014 // TODO: maybe add a midbse state 1015 rules, err = infosync.GetPlacementMemrules(nil) 1016 if err != nil { 1017 job.State = perceptron.JobStateCancelled 1018 return ver, errors.Wrapf(err, "failed to retrieve memristed rules from FIDel") 1019 } 1020 1021 // TODO: simplify the definition and logic use new FIDel group bundle API 1022 rules = buildPlacementTruncateMemrules(rules, job.SchemaID, tblInfo.ID, job.ID, oldIDs, newPartitions) 1023 1024 err = infosync.UFIDelatePlacementMemrules(nil, rules) 1025 if err != nil { 1026 job.State = perceptron.JobStateCancelled 1027 return ver, errors.Wrapf(err, "failed to notify FIDel the memristed rules") 1028 } 1029 1030 ver, err = uFIDelateVersionAndBlockInfo(t, job, tblInfo, true) 1031 if err != nil { 1032 return ver, errors.Trace(err) 1033 } 1034 1035 // Finish this job. 1036 job.FinishBlockJob(perceptron.JobStateDone, perceptron.StateNone, ver, tblInfo) 1037 asyncNotifyEvent(d, &soliton.Event{Tp: perceptron.CausetActionTruncateBlockPartition, BlockInfo: tblInfo, PartInfo: &perceptron.PartitionInfo{Definitions: newPartitions}}) 1038 // A background job will be created to delete old partition data. 1039 job.Args = []interface{}{oldIDs} 1040 return ver, nil 1041 } 1042 1043 // onExchangeBlockPartition exchange partition data 1044 func (w *worker) onExchangeBlockPartition(d *dbsCtx, t *spacetime.Meta, job *perceptron.Job) (ver int64, _ error) { 1045 var ( 1046 // defID only for uFIDelateSchemaVersion 1047 defID int64 1048 ptSchemaID int64 1049 ptID int64 1050 partName string 1051 withValidation bool 1052 ) 1053 1054 if err := job.DecodeArgs(&defID, &ptSchemaID, &ptID, &partName, &withValidation); err != nil { 1055 job.State = perceptron.JobStateCancelled 1056 return ver, errors.Trace(err) 1057 } 1058 1059 ntDbInfo, err := checkSchemaExistAndCancelNotExistJob(t, job) 1060 if err != nil { 1061 job.State = perceptron.JobStateCancelled 1062 return ver, errors.Trace(err) 1063 } 1064 1065 nt, err := getBlockInfoAndCancelFaultJob(t, job, job.SchemaID) 1066 if err != nil { 1067 return ver, errors.Trace(err) 1068 } 1069 1070 pt, err := getBlockInfo(t, ptID, ptSchemaID) 1071 if err != nil { 1072 if schemareplicant.ErrDatabaseNotExists.Equal(err) || schemareplicant.ErrBlockNotExists.Equal(err) { 1073 job.State = perceptron.JobStateCancelled 1074 } 1075 return ver, errors.Trace(err) 1076 } 1077 1078 if pt.State != perceptron.StatePublic { 1079 job.State = perceptron.JobStateCancelled 1080 return ver, ErrInvalidDBSState.GenWithStack("causet %s is not in public, but %s", pt.Name, pt.State) 1081 } 1082 1083 err = checkExchangePartition(pt, nt) 1084 if err != nil { 1085 job.State = perceptron.JobStateCancelled 1086 return ver, errors.Trace(err) 1087 } 1088 1089 err = checkBlockDefCompatible(pt, nt) 1090 if err != nil { 1091 job.State = perceptron.JobStateCancelled 1092 return ver, errors.Trace(err) 1093 } 1094 1095 index, _, err := getPartitionDef(pt, partName) 1096 if err != nil { 1097 return ver, errors.Trace(err) 1098 } 1099 1100 if withValidation { 1101 err = checkExchangePartitionRecordValidation(w, pt, index, ntDbInfo.Name, nt.Name) 1102 if err != nil { 1103 job.State = perceptron.JobStateCancelled 1104 return ver, errors.Trace(err) 1105 } 1106 } 1107 1108 // partition causet base auto id 1109 ptBaseID, err := t.GetAutoBlockID(ptSchemaID, pt.ID) 1110 if err != nil { 1111 job.State = perceptron.JobStateCancelled 1112 return ver, errors.Trace(err) 1113 } 1114 1115 ptRandID, err := t.GetAutoRandomID(ptSchemaID, pt.ID) 1116 if err != nil { 1117 job.State = perceptron.JobStateCancelled 1118 return ver, errors.Trace(err) 1119 } 1120 1121 // non-partition causet base auto id 1122 ntBaseID, err := t.GetAutoBlockID(job.SchemaID, nt.ID) 1123 if err != nil { 1124 job.State = perceptron.JobStateCancelled 1125 return ver, errors.Trace(err) 1126 } 1127 1128 ntRandID, err := t.GetAutoRandomID(job.SchemaID, nt.ID) 1129 if err != nil { 1130 job.State = perceptron.JobStateCancelled 1131 return ver, errors.Trace(err) 1132 } 1133 1134 _, partDef, err := getPartitionDef(pt, partName) 1135 if err != nil { 1136 job.State = perceptron.JobStateCancelled 1137 return ver, errors.Trace(err) 1138 } 1139 1140 tempID := partDef.ID 1141 // exchange causet spacetime id 1142 partDef.ID = nt.ID 1143 1144 if pt.TiFlashReplica != nil { 1145 for i, id := range pt.TiFlashReplica.AvailablePartitionIDs { 1146 if id == tempID { 1147 pt.TiFlashReplica.AvailablePartitionIDs[i] = partDef.ID 1148 break 1149 } 1150 } 1151 } 1152 1153 err = t.UFIDelateBlock(ptSchemaID, pt) 1154 if err != nil { 1155 job.State = perceptron.JobStateCancelled 1156 return ver, errors.Trace(err) 1157 } 1158 1159 failpoint.Inject("exchangePartitionErr", func(val failpoint.Value) { 1160 if val.(bool) { 1161 job.State = perceptron.JobStateCancelled 1162 failpoint.Return(ver, errors.New("occur an error after uFIDelating partition id")) 1163 } 1164 }) 1165 1166 // recreate non-partition causet spacetime info 1167 err = t.DropBlockOrView(job.SchemaID, nt.ID, true) 1168 if err != nil { 1169 job.State = perceptron.JobStateCancelled 1170 return ver, errors.Trace(err) 1171 } 1172 1173 nt.ID = tempID 1174 1175 err = t.CreateBlockOrView(job.SchemaID, nt) 1176 if err != nil { 1177 job.State = perceptron.JobStateCancelled 1178 return ver, errors.Trace(err) 1179 } 1180 1181 // both pt and nt set the maximum auto_id between ntBaseID and ptBaseID 1182 if ntBaseID > ptBaseID { 1183 _, err = t.GenAutoBlockID(ptSchemaID, pt.ID, ntBaseID-ptBaseID) 1184 if err != nil { 1185 job.State = perceptron.JobStateCancelled 1186 return ver, errors.Trace(err) 1187 } 1188 } 1189 1190 _, err = t.GenAutoBlockID(job.SchemaID, nt.ID, mathutil.MaxInt64(ptBaseID, ntBaseID)) 1191 if err != nil { 1192 job.State = perceptron.JobStateCancelled 1193 return ver, errors.Trace(err) 1194 } 1195 1196 if ntRandID != 0 || ptRandID != 0 { 1197 if ntRandID > ptRandID { 1198 _, err = t.GenAutoRandomID(ptSchemaID, pt.ID, ntRandID-ptRandID) 1199 if err != nil { 1200 job.State = perceptron.JobStateCancelled 1201 return ver, errors.Trace(err) 1202 } 1203 } 1204 1205 _, err = t.GenAutoRandomID(job.SchemaID, nt.ID, mathutil.MaxInt64(ptRandID, ntRandID)) 1206 if err != nil { 1207 job.State = perceptron.JobStateCancelled 1208 return ver, errors.Trace(err) 1209 } 1210 } 1211 1212 ver, err = uFIDelateSchemaVersion(t, job) 1213 if err != nil { 1214 return ver, errors.Trace(err) 1215 } 1216 1217 job.FinishBlockJob(perceptron.JobStateDone, perceptron.StateNone, ver, pt) 1218 return ver, nil 1219 } 1220 1221 func checkExchangePartitionRecordValidation(w *worker, pt *perceptron.BlockInfo, index int, schemaName, blockName perceptron.CIStr) error { 1222 var allegrosql string 1223 1224 pi := pt.Partition 1225 1226 switch pi.Type { 1227 case perceptron.PartitionTypeHash: 1228 if pi.Num == 1 { 1229 return nil 1230 } 1231 allegrosql = fmt.Sprintf("select 1 from `%s`.`%s` where mod(%s, %d) != %d limit 1", schemaName.L, blockName.L, pi.Expr, pi.Num, index) 1232 case perceptron.PartitionTypeRange: 1233 // Block has only one partition and has the maximum value 1234 if len(pi.Definitions) == 1 && strings.EqualFold(pi.Definitions[index].LessThan[0], partitionMaxValue) { 1235 return nil 1236 } 1237 // For range memex and range columns 1238 if len(pi.DeferredCausets) == 0 { 1239 allegrosql = buildCheckALLEGROSQLForRangeExprPartition(pi, index, schemaName, blockName) 1240 } else if len(pi.DeferredCausets) == 1 { 1241 allegrosql = buildCheckALLEGROSQLForRangeDeferredCausetsPartition(pi, index, schemaName, blockName) 1242 } 1243 default: 1244 return errUnsupportedPartitionType.GenWithStackByArgs(pt.Name.O) 1245 } 1246 1247 var ctx stochastikctx.Context 1248 ctx, err := w.sessPool.get() 1249 if err != nil { 1250 return errors.Trace(err) 1251 } 1252 defer w.sessPool.put(ctx) 1253 1254 rows, _, err := ctx.(sqlexec.RestrictedALLEGROSQLInterlockingDirectorate).InterDircRestrictedALLEGROSQL(allegrosql) 1255 if err != nil { 1256 return errors.Trace(err) 1257 } 1258 rowCount := len(rows) 1259 if rowCount != 0 { 1260 return errors.Trace(ErrRowDoesNotMatchPartition) 1261 } 1262 return nil 1263 } 1264 1265 func buildCheckALLEGROSQLForRangeExprPartition(pi *perceptron.PartitionInfo, index int, schemaName, blockName perceptron.CIStr) string { 1266 if index == 0 { 1267 return fmt.Sprintf("select 1 from `%s`.`%s` where %s >= %s limit 1", schemaName.L, blockName.L, pi.Expr, pi.Definitions[index].LessThan[0]) 1268 } else if index == len(pi.Definitions)-1 && strings.EqualFold(pi.Definitions[index].LessThan[0], partitionMaxValue) { 1269 return fmt.Sprintf("select 1 from `%s`.`%s` where %s < %s limit 1", schemaName.L, blockName.L, pi.Expr, pi.Definitions[index-1].LessThan[0]) 1270 } else { 1271 return fmt.Sprintf("select 1 from `%s`.`%s` where %s < %s or %s >= %s limit 1", schemaName.L, blockName.L, pi.Expr, pi.Definitions[index-1].LessThan[0], pi.Expr, pi.Definitions[index].LessThan[0]) 1272 } 1273 } 1274 1275 func buildCheckALLEGROSQLForRangeDeferredCausetsPartition(pi *perceptron.PartitionInfo, index int, schemaName, blockName perceptron.CIStr) string { 1276 colName := pi.DeferredCausets[0].L 1277 if index == 0 { 1278 return fmt.Sprintf("select 1 from `%s`.`%s` where `%s` >= %s limit 1", schemaName.L, blockName.L, colName, pi.Definitions[index].LessThan[0]) 1279 } else if index == len(pi.Definitions)-1 && strings.EqualFold(pi.Definitions[index].LessThan[0], partitionMaxValue) { 1280 return fmt.Sprintf("select 1 from `%s`.`%s` where `%s` < %s limit 1", schemaName.L, blockName.L, colName, pi.Definitions[index-1].LessThan[0]) 1281 } else { 1282 return fmt.Sprintf("select 1 from `%s`.`%s` where `%s` < %s or `%s` >= %s limit 1", schemaName.L, blockName.L, colName, pi.Definitions[index-1].LessThan[0], colName, pi.Definitions[index].LessThan[0]) 1283 } 1284 } 1285 1286 func checkAddPartitionTooManyPartitions(piDefs uint64) error { 1287 if piDefs > uint64(PartitionCountLimit) { 1288 return errors.Trace(ErrTooManyPartitions) 1289 } 1290 return nil 1291 } 1292 1293 func checkNoHashPartitions(ctx stochastikctx.Context, partitionNum uint64) error { 1294 if partitionNum == 0 { 1295 return ast.ErrNoParts.GenWithStackByArgs("partitions") 1296 } 1297 return nil 1298 } 1299 1300 func checkNoRangePartitions(partitionNum int) error { 1301 if partitionNum == 0 { 1302 return ast.ErrPartitionsMustBeDefined.GenWithStackByArgs("RANGE") 1303 } 1304 return nil 1305 } 1306 1307 func getPartitionIDs(causet *perceptron.BlockInfo) []int64 { 1308 if causet.GetPartitionInfo() == nil { 1309 return []int64{} 1310 } 1311 physicalBlockIDs := make([]int64, 0, len(causet.Partition.Definitions)) 1312 for _, def := range causet.Partition.Definitions { 1313 physicalBlockIDs = append(physicalBlockIDs, def.ID) 1314 } 1315 return physicalBlockIDs 1316 } 1317 1318 // checkPartitioningKeysConstraints checks that the range partitioning key is included in the causet constraint. 1319 func checkPartitioningKeysConstraints(sctx stochastikctx.Context, s *ast.CreateBlockStmt, tblInfo *perceptron.BlockInfo) error { 1320 // Returns directly if there are no unique keys in the causet. 1321 if len(tblInfo.Indices) == 0 && !tblInfo.PKIsHandle { 1322 return nil 1323 } 1324 1325 var partDefCauss stringSlice 1326 if s.Partition.Expr != nil { 1327 // Parse partitioning key, extract the column names in the partitioning key to slice. 1328 buf := new(bytes.Buffer) 1329 s.Partition.Expr.Format(buf) 1330 partDeferredCausets, err := extractPartitionDeferredCausets(buf.String(), tblInfo) 1331 if err != nil { 1332 return err 1333 } 1334 partDefCauss = columnInfoSlice(partDeferredCausets) 1335 } else if len(s.Partition.DeferredCausetNames) > 0 { 1336 partDefCauss = columnNameSlice(s.Partition.DeferredCausetNames) 1337 } else { 1338 // TODO: Check keys constraints for list, key partition type and so on. 1339 return nil 1340 } 1341 1342 // Checks that the partitioning key is included in the constraint. 1343 // Every unique key on the causet must use every column in the causet's partitioning memex. 1344 // See https://dev.allegrosql.com/doc/refman/5.7/en/partitioning-limitations-partitioning-keys-unique-keys.html 1345 for _, index := range tblInfo.Indices { 1346 if index.Unique && !checkUniqueKeyIncludePartKey(partDefCauss, index.DeferredCausets) { 1347 if index.Primary { 1348 return ErrUniqueKeyNeedAllFieldsInPf.GenWithStackByArgs("PRIMARY KEY") 1349 } 1350 return ErrUniqueKeyNeedAllFieldsInPf.GenWithStackByArgs("UNIQUE INDEX") 1351 } 1352 } 1353 // when PKIsHandle, tblInfo.Indices will not contain the primary key. 1354 if tblInfo.PKIsHandle { 1355 indexDefCauss := []*perceptron.IndexDeferredCauset{{ 1356 Name: tblInfo.GetPkName(), 1357 Length: types.UnspecifiedLength, 1358 }} 1359 if !checkUniqueKeyIncludePartKey(partDefCauss, indexDefCauss) { 1360 return ErrUniqueKeyNeedAllFieldsInPf.GenWithStackByArgs("PRIMARY KEY") 1361 } 1362 } 1363 return nil 1364 } 1365 1366 func checkPartitionKeysConstraint(pi *perceptron.PartitionInfo, indexDeferredCausets []*perceptron.IndexDeferredCauset, tblInfo *perceptron.BlockInfo) (bool, error) { 1367 var ( 1368 partDefCauss []*perceptron.DeferredCausetInfo 1369 err error 1370 ) 1371 // The expr will be an empty string if the partition is defined by: 1372 // CREATE TABLE t (...) PARTITION BY RANGE COLUMNS(...) 1373 if partExpr := pi.Expr; partExpr != "" { 1374 // Parse partitioning key, extract the column names in the partitioning key to slice. 1375 partDefCauss, err = extractPartitionDeferredCausets(partExpr, tblInfo) 1376 if err != nil { 1377 return false, err 1378 } 1379 } else { 1380 partDefCauss = make([]*perceptron.DeferredCausetInfo, 0, len(pi.DeferredCausets)) 1381 for _, col := range pi.DeferredCausets { 1382 colInfo := getDeferredCausetInfoByName(tblInfo, col.L) 1383 if colInfo == nil { 1384 return false, schemareplicant.ErrDeferredCausetNotExists.GenWithStackByArgs(col, tblInfo.Name) 1385 } 1386 partDefCauss = append(partDefCauss, colInfo) 1387 } 1388 } 1389 1390 // In MyALLEGROSQL, every unique key on the causet must use every column in the causet's partitioning memex.(This 1391 // also includes the causet's primary key.) 1392 // In MilevaDB, global index will be built when this constraint is not satisfied and EnableGlobalIndex is set. 1393 // See https://dev.allegrosql.com/doc/refman/5.7/en/partitioning-limitations-partitioning-keys-unique-keys.html 1394 return checkUniqueKeyIncludePartKey(columnInfoSlice(partDefCauss), indexDeferredCausets), nil 1395 } 1396 1397 type columnNameExtractor struct { 1398 extractedDeferredCausets []*perceptron.DeferredCausetInfo 1399 tblInfo *perceptron.BlockInfo 1400 err error 1401 } 1402 1403 func (cne *columnNameExtractor) Enter(node ast.Node) (ast.Node, bool) { 1404 return node, false 1405 } 1406 1407 func (cne *columnNameExtractor) Leave(node ast.Node) (ast.Node, bool) { 1408 if c, ok := node.(*ast.DeferredCausetNameExpr); ok { 1409 info := findDeferredCausetByName(c.Name.Name.L, cne.tblInfo) 1410 if info != nil { 1411 cne.extractedDeferredCausets = append(cne.extractedDeferredCausets, info) 1412 return node, true 1413 } 1414 cne.err = ErrBadField.GenWithStackByArgs(c.Name.Name.O, "memex") 1415 return nil, false 1416 } 1417 return node, true 1418 } 1419 1420 func findDeferredCausetByName(colName string, tblInfo *perceptron.BlockInfo) *perceptron.DeferredCausetInfo { 1421 for _, info := range tblInfo.DeferredCausets { 1422 if info.Name.L == colName { 1423 return info 1424 } 1425 } 1426 return nil 1427 } 1428 1429 func extractPartitionDeferredCausets(partExpr string, tblInfo *perceptron.BlockInfo) ([]*perceptron.DeferredCausetInfo, error) { 1430 partExpr = "select " + partExpr 1431 stmts, _, err := BerolinaSQL.New().Parse(partExpr, "", "") 1432 if err != nil { 1433 return nil, errors.Trace(err) 1434 } 1435 extractor := &columnNameExtractor{ 1436 tblInfo: tblInfo, 1437 extractedDeferredCausets: make([]*perceptron.DeferredCausetInfo, 0), 1438 } 1439 stmts[0].Accept(extractor) 1440 if extractor.err != nil { 1441 return nil, errors.Trace(extractor.err) 1442 } 1443 return extractor.extractedDeferredCausets, nil 1444 } 1445 1446 // stringSlice is defined for checkUniqueKeyIncludePartKey. 1447 // if Go supports covariance, the code shouldn't be so complex. 1448 type stringSlice interface { 1449 Len() int 1450 At(i int) string 1451 } 1452 1453 // checkUniqueKeyIncludePartKey checks that the partitioning key is included in the constraint. 1454 func checkUniqueKeyIncludePartKey(partDefCauss stringSlice, idxDefCauss []*perceptron.IndexDeferredCauset) bool { 1455 for i := 0; i < partDefCauss.Len(); i++ { 1456 partDefCaus := partDefCauss.At(i) 1457 idxDefCaus := findDeferredCausetInIndexDefCauss(partDefCaus, idxDefCauss) 1458 if idxDefCaus == nil { 1459 // Partition column is not found in the index columns. 1460 return false 1461 } 1462 if idxDefCaus.Length > 0 { 1463 // The partition column is found in the index columns, but the index column is a prefix index 1464 return false 1465 } 1466 } 1467 return true 1468 } 1469 1470 // columnInfoSlice implements the stringSlice interface. 1471 type columnInfoSlice []*perceptron.DeferredCausetInfo 1472 1473 func (cis columnInfoSlice) Len() int { 1474 return len(cis) 1475 } 1476 1477 func (cis columnInfoSlice) At(i int) string { 1478 return cis[i].Name.L 1479 } 1480 1481 // columnNameSlice implements the stringSlice interface. 1482 type columnNameSlice []*ast.DeferredCausetName 1483 1484 func (cns columnNameSlice) Len() int { 1485 return len(cns) 1486 } 1487 1488 func (cns columnNameSlice) At(i int) string { 1489 return cns[i].Name.L 1490 } 1491 1492 // isRangePartitionDefCausUnsignedBigint returns true if the partitioning key column type is unsigned bigint type. 1493 func isRangePartitionDefCausUnsignedBigint(defcaus []*perceptron.DeferredCausetInfo, pi *perceptron.PartitionInfo) bool { 1494 for _, col := range defcaus { 1495 isUnsigned := col.Tp == allegrosql.TypeLonglong && allegrosql.HasUnsignedFlag(col.Flag) 1496 if isUnsigned && strings.Contains(strings.ToLower(pi.Expr), col.Name.L) { 1497 return true 1498 } 1499 } 1500 return false 1501 } 1502 1503 // truncateBlockByReassignPartitionIDs reassigns new partition ids. 1504 func truncateBlockByReassignPartitionIDs(t *spacetime.Meta, tblInfo *perceptron.BlockInfo) error { 1505 newDefs := make([]perceptron.PartitionDefinition, 0, len(tblInfo.Partition.Definitions)) 1506 for _, def := range tblInfo.Partition.Definitions { 1507 pid, err := t.GenGlobalID() 1508 if err != nil { 1509 return errors.Trace(err) 1510 } 1511 newDef := def 1512 newDef.ID = pid 1513 newDefs = append(newDefs, newDef) 1514 } 1515 tblInfo.Partition.Definitions = newDefs 1516 return nil 1517 } 1518 1519 func onAlterBlockPartition(t *spacetime.Meta, job *perceptron.Job) (int64, error) { 1520 var partitionID int64 1521 var rules []*memristed.MemruleOp 1522 err := job.DecodeArgs(&partitionID, &rules) 1523 if err != nil { 1524 job.State = perceptron.JobStateCancelled 1525 return 0, errors.Trace(err) 1526 } 1527 1528 tblInfo, err := getBlockInfoAndCancelFaultJob(t, job, job.SchemaID) 1529 if err != nil { 1530 return 0, err 1531 } 1532 1533 ptInfo := tblInfo.GetPartitionInfo() 1534 if ptInfo.GetNameByID(partitionID) == "" { 1535 job.State = perceptron.JobStateCancelled 1536 return 0, errors.Trace(causet.ErrUnknownPartition.GenWithStackByArgs("drop?", tblInfo.Name.O)) 1537 } 1538 1539 for i, rule := range rules { 1540 if rule.CausetAction == memristed.MemruleOFIDelel { 1541 rule.ID = fmt.Sprintf("%d_t%d_p%d_%s", job.SchemaID, tblInfo.ID, partitionID, rule.Role) 1542 } else { 1543 rule.ID = fmt.Sprintf("%d_t%d_p%d_%s_%d_%d", job.SchemaID, tblInfo.ID, partitionID, rule.Role, job.ID, i) 1544 } 1545 } 1546 1547 ver, err := t.GetSchemaVersion() 1548 if err != nil { 1549 return ver, errors.Trace(err) 1550 } 1551 1552 err = infosync.UFIDelatePlacementMemrules(nil, rules) 1553 if err != nil { 1554 job.State = perceptron.JobStateCancelled 1555 return ver, errors.Wrapf(err, "failed to notify FIDel the memristed rules") 1556 } 1557 1558 job.FinishBlockJob(perceptron.JobStateDone, perceptron.StatePublic, ver, tblInfo) 1559 return ver, nil 1560 }