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  }