github.com/whtcorpsinc/MilevaDB-Prod@v0.0.0-20211104133533-f57f4be3b597/interlock/ddl.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 interlock
    15  
    16  import (
    17  	"context"
    18  	"fmt"
    19  	"strings"
    20  
    21  	"github.com/whtcorpsinc/BerolinaSQL/allegrosql"
    22  	"github.com/whtcorpsinc/BerolinaSQL/ast"
    23  	"github.com/whtcorpsinc/BerolinaSQL/perceptron"
    24  	"github.com/whtcorpsinc/BerolinaSQL/terror"
    25  	"github.com/whtcorpsinc/errors"
    26  	"github.com/whtcorpsinc/milevadb/causet/embedded"
    27  	"github.com/whtcorpsinc/milevadb/config"
    28  	"github.com/whtcorpsinc/milevadb/dbs"
    29  	"github.com/whtcorpsinc/milevadb/petri"
    30  	"github.com/whtcorpsinc/milevadb/schemareplicant"
    31  	"github.com/whtcorpsinc/milevadb/soliton/admin"
    32  	"github.com/whtcorpsinc/milevadb/soliton/chunk"
    33  	"github.com/whtcorpsinc/milevadb/soliton/gcutil"
    34  	"github.com/whtcorpsinc/milevadb/soliton/logutil"
    35  	"github.com/whtcorpsinc/milevadb/soliton/sqlexec"
    36  	"github.com/whtcorpsinc/milevadb/spacetime"
    37  	"github.com/whtcorpsinc/milevadb/stochastikctx/variable"
    38  	"github.com/whtcorpsinc/milevadb/types"
    39  	"go.uber.org/zap"
    40  )
    41  
    42  // DBSInterDirc represents a DBS interlock.
    43  // It grabs a DBS instance from Petri, calling the DBS methods to do the work.
    44  type DBSInterDirc struct {
    45  	baseInterlockingDirectorate
    46  
    47  	stmt ast.StmtNode
    48  	is   schemareplicant.SchemaReplicant
    49  	done bool
    50  }
    51  
    52  // toErr converts the error to the ErrSchemaReplicantChanged when the schemaReplicant is outdated.
    53  func (e *DBSInterDirc) toErr(err error) error {
    54  	// The err may be cause by schemaReplicant changed, here we distinguish the ErrSchemaReplicantChanged error from other errors.
    55  	dom := petri.GetPetri(e.ctx)
    56  	checker := petri.NewSchemaChecker(dom, e.is.SchemaMetaVersion(), nil)
    57  	txn, err1 := e.ctx.Txn(true)
    58  	if err1 != nil {
    59  		logutil.BgLogger().Error("active txn failed", zap.Error(err))
    60  		return err1
    61  	}
    62  	_, schemaInfoErr := checker.Check(txn.StartTS())
    63  	if schemaInfoErr != nil {
    64  		return errors.Trace(schemaInfoErr)
    65  	}
    66  	return err
    67  }
    68  
    69  // Next implements the InterlockingDirectorate Next interface.
    70  func (e *DBSInterDirc) Next(ctx context.Context, req *chunk.Chunk) (err error) {
    71  	if e.done {
    72  		return nil
    73  	}
    74  	e.done = true
    75  
    76  	// For each DBS, we should commit the previous transaction and create a new transaction.
    77  	if err = e.ctx.NewTxn(ctx); err != nil {
    78  		return err
    79  	}
    80  	defer func() { e.ctx.GetStochastikVars().StmtCtx.IsDBSJobInQueue = false }()
    81  
    82  	switch x := e.stmt.(type) {
    83  	case *ast.AlterDatabaseStmt:
    84  		err = e.executeAlterDatabase(x)
    85  	case *ast.AlterBlockStmt:
    86  		err = e.executeAlterBlock(x)
    87  	case *ast.CreateIndexStmt:
    88  		err = e.executeCreateIndex(x)
    89  	case *ast.CreateDatabaseStmt:
    90  		err = e.executeCreateDatabase(x)
    91  	case *ast.CreateBlockStmt:
    92  		err = e.executeCreateBlock(x)
    93  	case *ast.CreateViewStmt:
    94  		err = e.executeCreateView(x)
    95  	case *ast.DropIndexStmt:
    96  		err = e.executeDropIndex(x)
    97  	case *ast.DroFIDelatabaseStmt:
    98  		err = e.executeDroFIDelatabase(x)
    99  	case *ast.DropBlockStmt:
   100  		if x.IsView {
   101  			err = e.executeDropView(x)
   102  		} else {
   103  			err = e.executeDropBlock(x)
   104  		}
   105  	case *ast.RecoverBlockStmt:
   106  		err = e.executeRecoverBlock(x)
   107  	case *ast.FlashBackBlockStmt:
   108  		err = e.executeFlashbackBlock(x)
   109  	case *ast.RenameBlockStmt:
   110  		err = e.executeRenameBlock(x)
   111  	case *ast.TruncateBlockStmt:
   112  		err = e.executeTruncateBlock(x)
   113  	case *ast.LockBlocksStmt:
   114  		err = e.executeLockBlocks(x)
   115  	case *ast.UnlockBlocksStmt:
   116  		err = e.executeUnlockBlocks(x)
   117  	case *ast.CleanupBlockLockStmt:
   118  		err = e.executeCleanupBlockLock(x)
   119  	case *ast.RepairBlockStmt:
   120  		err = e.executeRepairBlock(x)
   121  	case *ast.CreateSequenceStmt:
   122  		err = e.executeCreateSequence(x)
   123  	case *ast.DropSequenceStmt:
   124  		err = e.executeDropSequence(x)
   125  
   126  	}
   127  	if err != nil {
   128  		// If the tenant return ErrBlockNotExists error when running this DBS, it may be caused by schemaReplicant changed,
   129  		// otherwise, ErrBlockNotExists can be returned before putting this DBS job to the job queue.
   130  		if (e.ctx.GetStochastikVars().StmtCtx.IsDBSJobInQueue && schemareplicant.ErrBlockNotExists.Equal(err)) ||
   131  			!e.ctx.GetStochastikVars().StmtCtx.IsDBSJobInQueue {
   132  			return e.toErr(err)
   133  		}
   134  		return err
   135  
   136  	}
   137  
   138  	dom := petri.GetPetri(e.ctx)
   139  	// UFIDelate SchemaReplicant in TxnCtx, so it will pass schemaReplicant check.
   140  	is := dom.SchemaReplicant()
   141  	txnCtx := e.ctx.GetStochastikVars().TxnCtx
   142  	txnCtx.SchemaReplicant = is
   143  	txnCtx.SchemaVersion = is.SchemaMetaVersion()
   144  	// DBS will force commit old transaction, after DBS, in transaction status should be false.
   145  	e.ctx.GetStochastikVars().SetStatusFlag(allegrosql.ServerStatusInTrans, false)
   146  	return nil
   147  }
   148  
   149  func (e *DBSInterDirc) executeTruncateBlock(s *ast.TruncateBlockStmt) error {
   150  	ident := ast.Ident{Schema: s.Block.Schema, Name: s.Block.Name}
   151  	err := petri.GetPetri(e.ctx).DBS().TruncateBlock(e.ctx, ident)
   152  	return err
   153  }
   154  
   155  func (e *DBSInterDirc) executeRenameBlock(s *ast.RenameBlockStmt) error {
   156  	if len(s.BlockToBlocks) != 1 {
   157  		// Now we only allow one schemaReplicant changing at the same time.
   158  		return errors.Errorf("can't run multi schemaReplicant change")
   159  	}
   160  	oldIdent := ast.Ident{Schema: s.OldBlock.Schema, Name: s.OldBlock.Name}
   161  	newIdent := ast.Ident{Schema: s.NewBlock.Schema, Name: s.NewBlock.Name}
   162  	isAlterBlock := false
   163  	err := petri.GetPetri(e.ctx).DBS().RenameBlock(e.ctx, oldIdent, newIdent, isAlterBlock)
   164  	return err
   165  }
   166  
   167  func (e *DBSInterDirc) executeCreateDatabase(s *ast.CreateDatabaseStmt) error {
   168  	var opt *ast.CharsetOpt
   169  	if len(s.Options) != 0 {
   170  		opt = &ast.CharsetOpt{}
   171  		for _, val := range s.Options {
   172  			switch val.Tp {
   173  			case ast.DatabaseOptionCharset:
   174  				opt.Chs = val.Value
   175  			case ast.DatabaseOptionDefCauslate:
   176  				opt.DefCaus = val.Value
   177  			}
   178  		}
   179  	}
   180  	err := petri.GetPetri(e.ctx).DBS().CreateSchema(e.ctx, perceptron.NewCIStr(s.Name), opt)
   181  	if err != nil {
   182  		if schemareplicant.ErrDatabaseExists.Equal(err) && s.IfNotExists {
   183  			err = nil
   184  		}
   185  	}
   186  	return err
   187  }
   188  
   189  func (e *DBSInterDirc) executeAlterDatabase(s *ast.AlterDatabaseStmt) error {
   190  	err := petri.GetPetri(e.ctx).DBS().AlterSchema(e.ctx, s)
   191  	return err
   192  }
   193  
   194  func (e *DBSInterDirc) executeCreateBlock(s *ast.CreateBlockStmt) error {
   195  	err := petri.GetPetri(e.ctx).DBS().CreateBlock(e.ctx, s)
   196  	return err
   197  }
   198  
   199  func (e *DBSInterDirc) executeCreateView(s *ast.CreateViewStmt) error {
   200  	err := petri.GetPetri(e.ctx).DBS().CreateView(e.ctx, s)
   201  	return err
   202  }
   203  
   204  func (e *DBSInterDirc) executeCreateIndex(s *ast.CreateIndexStmt) error {
   205  	ident := ast.Ident{Schema: s.Block.Schema, Name: s.Block.Name}
   206  	err := petri.GetPetri(e.ctx).DBS().CreateIndex(e.ctx, ident, s.KeyType, perceptron.NewCIStr(s.IndexName),
   207  		s.IndexPartSpecifications, s.IndexOption, s.IfNotExists)
   208  	return err
   209  }
   210  
   211  func (e *DBSInterDirc) executeDroFIDelatabase(s *ast.DroFIDelatabaseStmt) error {
   212  	dbName := perceptron.NewCIStr(s.Name)
   213  
   214  	// Protect important system causet from been dropped by a mistake.
   215  	// I can hardly find a case that a user really need to do this.
   216  	if dbName.L == "allegrosql" {
   217  		return errors.New("Drop 'allegrosql' database is forbidden")
   218  	}
   219  
   220  	err := petri.GetPetri(e.ctx).DBS().DropSchema(e.ctx, dbName)
   221  	if schemareplicant.ErrDatabaseNotExists.Equal(err) {
   222  		if s.IfExists {
   223  			err = nil
   224  		} else {
   225  			err = schemareplicant.ErrDatabaseDropExists.GenWithStackByArgs(s.Name)
   226  		}
   227  	}
   228  	stochastikVars := e.ctx.GetStochastikVars()
   229  	if err == nil && strings.ToLower(stochastikVars.CurrentDB) == dbName.L {
   230  		stochastikVars.CurrentDB = ""
   231  		err = variable.SetStochastikSystemVar(stochastikVars, variable.CharsetDatabase, types.NewStringCauset(allegrosql.DefaultCharset))
   232  		if err != nil {
   233  			return err
   234  		}
   235  		err = variable.SetStochastikSystemVar(stochastikVars, variable.DefCauslationDatabase, types.NewStringCauset(allegrosql.DefaultDefCauslationName))
   236  		if err != nil {
   237  			return err
   238  		}
   239  	}
   240  	return err
   241  }
   242  
   243  // If one drop those blocks by mistake, it's difficult to recover.
   244  // In the worst case, the whole MilevaDB cluster fails to bootstrap, so we prevent user from dropping them.
   245  var systemBlocks = map[string]struct{}{
   246  	"milevadb":             {},
   247  	"gc_delete_range":      {},
   248  	"gc_delete_range_done": {},
   249  }
   250  
   251  func isSystemBlock(schemaReplicant, causet string) bool {
   252  	if schemaReplicant != "allegrosql" {
   253  		return false
   254  	}
   255  	if _, ok := systemBlocks[causet]; ok {
   256  		return true
   257  	}
   258  	return false
   259  }
   260  
   261  type objectType int
   262  
   263  const (
   264  	blockObject objectType = iota
   265  	viewObject
   266  	sequenceObject
   267  )
   268  
   269  func (e *DBSInterDirc) executeDropBlock(s *ast.DropBlockStmt) error {
   270  	return e.dropBlockObject(s.Blocks, blockObject, s.IfExists)
   271  }
   272  
   273  func (e *DBSInterDirc) executeDropView(s *ast.DropBlockStmt) error {
   274  	return e.dropBlockObject(s.Blocks, viewObject, s.IfExists)
   275  }
   276  
   277  func (e *DBSInterDirc) executeDropSequence(s *ast.DropSequenceStmt) error {
   278  	return e.dropBlockObject(s.Sequences, sequenceObject, s.IfExists)
   279  }
   280  
   281  // dropBlockObject actually applies to `blockObject`, `viewObject` and `sequenceObject`.
   282  func (e *DBSInterDirc) dropBlockObject(objects []*ast.BlockName, obt objectType, ifExists bool) error {
   283  	var notExistBlocks []string
   284  	for _, tn := range objects {
   285  		fullti := ast.Ident{Schema: tn.Schema, Name: tn.Name}
   286  		_, ok := e.is.SchemaByName(tn.Schema)
   287  		if !ok {
   288  			// TODO: we should return special error for causet not exist, checking "not exist" is not enough,
   289  			// because some other errors may contain this error string too.
   290  			notExistBlocks = append(notExistBlocks, fullti.String())
   291  			continue
   292  		}
   293  		_, err := e.is.BlockByName(tn.Schema, tn.Name)
   294  		if err != nil && schemareplicant.ErrBlockNotExists.Equal(err) {
   295  			notExistBlocks = append(notExistBlocks, fullti.String())
   296  			continue
   297  		} else if err != nil {
   298  			return err
   299  		}
   300  
   301  		// Protect important system causet from been dropped by a mistake.
   302  		// I can hardly find a case that a user really need to do this.
   303  		if isSystemBlock(tn.Schema.L, tn.Name.L) {
   304  			return errors.Errorf("Drop milevadb system causet '%s.%s' is forbidden", tn.Schema.L, tn.Name.L)
   305  		}
   306  
   307  		if obt == blockObject && config.CheckBlockBeforeDrop {
   308  			logutil.BgLogger().Warn("admin check causet before drop",
   309  				zap.String("database", fullti.Schema.O),
   310  				zap.String("causet", fullti.Name.O),
   311  			)
   312  			allegrosql := fmt.Sprintf("admin check causet `%s`.`%s`", fullti.Schema.O, fullti.Name.O)
   313  			_, _, err = e.ctx.(sqlexec.RestrictedALLEGROSQLInterlockingDirectorate).InterDircRestrictedALLEGROSQL(allegrosql)
   314  			if err != nil {
   315  				return err
   316  			}
   317  		}
   318  		switch obt {
   319  		case blockObject:
   320  			err = petri.GetPetri(e.ctx).DBS().DropBlock(e.ctx, fullti)
   321  		case viewObject:
   322  			err = petri.GetPetri(e.ctx).DBS().DropView(e.ctx, fullti)
   323  		case sequenceObject:
   324  			err = petri.GetPetri(e.ctx).DBS().DropSequence(e.ctx, fullti, ifExists)
   325  		}
   326  		if schemareplicant.ErrDatabaseNotExists.Equal(err) || schemareplicant.ErrBlockNotExists.Equal(err) {
   327  			notExistBlocks = append(notExistBlocks, fullti.String())
   328  		} else if err != nil {
   329  			return err
   330  		}
   331  	}
   332  	if len(notExistBlocks) > 0 && !ifExists {
   333  		if obt == sequenceObject {
   334  			return schemareplicant.ErrSequenceDropExists.GenWithStackByArgs(strings.Join(notExistBlocks, ","))
   335  		}
   336  		return schemareplicant.ErrBlockDropExists.GenWithStackByArgs(strings.Join(notExistBlocks, ","))
   337  	}
   338  	// We need add warning when use if exists.
   339  	if len(notExistBlocks) > 0 && ifExists {
   340  		for _, causet := range notExistBlocks {
   341  			if obt == sequenceObject {
   342  				e.ctx.GetStochastikVars().StmtCtx.AppendNote(schemareplicant.ErrSequenceDropExists.GenWithStackByArgs(causet))
   343  			} else {
   344  				e.ctx.GetStochastikVars().StmtCtx.AppendNote(schemareplicant.ErrBlockDropExists.GenWithStackByArgs(causet))
   345  			}
   346  		}
   347  	}
   348  	return nil
   349  }
   350  
   351  func (e *DBSInterDirc) executeDropIndex(s *ast.DropIndexStmt) error {
   352  	ti := ast.Ident{Schema: s.Block.Schema, Name: s.Block.Name}
   353  	err := petri.GetPetri(e.ctx).DBS().DropIndex(e.ctx, ti, perceptron.NewCIStr(s.IndexName), s.IfExists)
   354  	if (schemareplicant.ErrDatabaseNotExists.Equal(err) || schemareplicant.ErrBlockNotExists.Equal(err)) && s.IfExists {
   355  		err = nil
   356  	}
   357  	return err
   358  }
   359  
   360  func (e *DBSInterDirc) executeAlterBlock(s *ast.AlterBlockStmt) error {
   361  	ti := ast.Ident{Schema: s.Block.Schema, Name: s.Block.Name}
   362  	err := petri.GetPetri(e.ctx).DBS().AlterBlock(e.ctx, ti, s.Specs)
   363  	return err
   364  }
   365  
   366  // executeRecoverBlock represents a recover causet interlock.
   367  // It is built from "recover causet" memex,
   368  // is used to recover the causet that deleted by mistake.
   369  func (e *DBSInterDirc) executeRecoverBlock(s *ast.RecoverBlockStmt) error {
   370  	txn, err := e.ctx.Txn(true)
   371  	if err != nil {
   372  		return err
   373  	}
   374  	t := spacetime.NewMeta(txn)
   375  	dom := petri.GetPetri(e.ctx)
   376  	var job *perceptron.Job
   377  	var tblInfo *perceptron.BlockInfo
   378  	if s.JobID != 0 {
   379  		job, tblInfo, err = e.getRecoverBlockByJobID(s, t, dom)
   380  	} else {
   381  		job, tblInfo, err = e.getRecoverBlockByBlockName(s.Block)
   382  	}
   383  	if err != nil {
   384  		return err
   385  	}
   386  	// Check the causet ID was not exists.
   387  	tbl, ok := dom.SchemaReplicant().BlockByID(tblInfo.ID)
   388  	if ok {
   389  		return schemareplicant.ErrBlockExists.GenWithStack("Block '%-.192s' already been recover to '%-.192s', can't be recover repeatedly", s.Block.Name.O, tbl.Meta().Name.O)
   390  	}
   391  
   392  	autoIncID, autoRandID, err := e.getBlockAutoIDsFromSnapshot(job)
   393  	if err != nil {
   394  		return err
   395  	}
   396  
   397  	recoverInfo := &dbs.RecoverInfo{
   398  		SchemaID:      job.SchemaID,
   399  		BlockInfo:     tblInfo,
   400  		DropJobID:     job.ID,
   401  		SnapshotTS:    job.StartTS,
   402  		CurAutoIncID:  autoIncID,
   403  		CurAutoRandID: autoRandID,
   404  	}
   405  	// Call DBS RecoverBlock.
   406  	err = petri.GetPetri(e.ctx).DBS().RecoverBlock(e.ctx, recoverInfo)
   407  	return err
   408  }
   409  
   410  func (e *DBSInterDirc) getBlockAutoIDsFromSnapshot(job *perceptron.Job) (autoIncID, autoRandID int64, err error) {
   411  	// Get causet original autoIDs before causet drop.
   412  	dom := petri.GetPetri(e.ctx)
   413  	m, err := dom.GetSnapshotMeta(job.StartTS)
   414  	if err != nil {
   415  		return 0, 0, err
   416  	}
   417  	autoIncID, err = m.GetAutoBlockID(job.SchemaID, job.BlockID)
   418  	if err != nil {
   419  		return 0, 0, errors.Errorf("recover block_id: %d, get original autoIncID from snapshot spacetime err: %s", job.BlockID, err.Error())
   420  	}
   421  	autoRandID, err = m.GetAutoRandomID(job.SchemaID, job.BlockID)
   422  	if err != nil {
   423  		return 0, 0, errors.Errorf("recover block_id: %d, get original autoRandID from snapshot spacetime err: %s", job.BlockID, err.Error())
   424  	}
   425  	return autoIncID, autoRandID, nil
   426  }
   427  
   428  func (e *DBSInterDirc) getRecoverBlockByJobID(s *ast.RecoverBlockStmt, t *spacetime.Meta, dom *petri.Petri) (*perceptron.Job, *perceptron.BlockInfo, error) {
   429  	job, err := t.GetHistoryDBSJob(s.JobID)
   430  	if err != nil {
   431  		return nil, nil, err
   432  	}
   433  	if job == nil {
   434  		return nil, nil, admin.ErrDBSJobNotFound.GenWithStackByArgs(s.JobID)
   435  	}
   436  	if job.Type != perceptron.CausetActionDropBlock && job.Type != perceptron.CausetActionTruncateBlock {
   437  		return nil, nil, errors.Errorf("Job %v type is %v, not dropped/truncated causet", job.ID, job.Type)
   438  	}
   439  
   440  	// Check GC safe point for getting snapshot schemaReplicant.
   441  	err = gcutil.ValidateSnapshot(e.ctx, job.StartTS)
   442  	if err != nil {
   443  		return nil, nil, err
   444  	}
   445  
   446  	// Get the snapshot schemaReplicant before drop causet.
   447  	snapInfo, err := dom.GetSnapshotSchemaReplicant(job.StartTS)
   448  	if err != nil {
   449  		return nil, nil, err
   450  	}
   451  	// Get causet spacetime from snapshot schemaReplicant.
   452  	causet, ok := snapInfo.BlockByID(job.BlockID)
   453  	if !ok {
   454  		return nil, nil, schemareplicant.ErrBlockNotExists.GenWithStackByArgs(
   455  			fmt.Sprintf("(Schema ID %d)", job.SchemaID),
   456  			fmt.Sprintf("(Block ID %d)", job.BlockID),
   457  		)
   458  	}
   459  	return job, causet.Meta(), nil
   460  }
   461  
   462  // GetDropOrTruncateBlockInfoFromJobs gets the dropped/truncated causet information from DBS jobs,
   463  // it will use the `start_ts` of DBS job as snapshot to get the dropped/truncated causet information.
   464  func GetDropOrTruncateBlockInfoFromJobs(jobs []*perceptron.Job, gcSafePoint uint64, dom *petri.Petri, fn func(*perceptron.Job, *perceptron.BlockInfo) (bool, error)) (bool, error) {
   465  	for _, job := range jobs {
   466  		// Check GC safe point for getting snapshot schemaReplicant.
   467  		err := gcutil.ValidateSnapshotWithGCSafePoint(job.StartTS, gcSafePoint)
   468  		if err != nil {
   469  			return false, err
   470  		}
   471  		if job.Type != perceptron.CausetActionDropBlock && job.Type != perceptron.CausetActionTruncateBlock {
   472  			continue
   473  		}
   474  
   475  		snapMeta, err := dom.GetSnapshotMeta(job.StartTS)
   476  		if err != nil {
   477  			return false, err
   478  		}
   479  		tbl, err := snapMeta.GetBlock(job.SchemaID, job.BlockID)
   480  		if err != nil {
   481  			if spacetime.ErrDBNotExists.Equal(err) {
   482  				// The dropped/truncated DBS maybe execute failed that caused by the parallel DBS execution,
   483  				// then can't find the causet from the snapshot info-schemaReplicant. Should just ignore error here,
   484  				// see more in TestParallelDropSchemaAndDropBlock.
   485  				continue
   486  			}
   487  			return false, err
   488  		}
   489  		if tbl == nil {
   490  			// The dropped/truncated DBS maybe execute failed that caused by the parallel DBS execution,
   491  			// then can't find the causet from the snapshot info-schemaReplicant. Should just ignore error here,
   492  			// see more in TestParallelDropSchemaAndDropBlock.
   493  			continue
   494  		}
   495  		finish, err := fn(job, tbl)
   496  		if err != nil || finish {
   497  			return finish, err
   498  		}
   499  	}
   500  	return false, nil
   501  }
   502  
   503  func (e *DBSInterDirc) getRecoverBlockByBlockName(blockName *ast.BlockName) (*perceptron.Job, *perceptron.BlockInfo, error) {
   504  	txn, err := e.ctx.Txn(true)
   505  	if err != nil {
   506  		return nil, nil, err
   507  	}
   508  	schemaName := blockName.Schema.L
   509  	if schemaName == "" {
   510  		schemaName = strings.ToLower(e.ctx.GetStochastikVars().CurrentDB)
   511  	}
   512  	if schemaName == "" {
   513  		return nil, nil, errors.Trace(embedded.ErrNoDB)
   514  	}
   515  	gcSafePoint, err := gcutil.GetGCSafePoint(e.ctx)
   516  	if err != nil {
   517  		return nil, nil, err
   518  	}
   519  	var jobInfo *perceptron.Job
   520  	var blockInfo *perceptron.BlockInfo
   521  	dom := petri.GetPetri(e.ctx)
   522  	handleJobAndBlockInfo := func(job *perceptron.Job, tblInfo *perceptron.BlockInfo) (bool, error) {
   523  		if tblInfo.Name.L != blockName.Name.L {
   524  			return false, nil
   525  		}
   526  		schemaReplicant, ok := dom.SchemaReplicant().SchemaByID(job.SchemaID)
   527  		if !ok {
   528  			return false, nil
   529  		}
   530  		if schemaReplicant.Name.L == schemaName {
   531  			blockInfo = tblInfo
   532  			jobInfo = job
   533  			return true, nil
   534  		}
   535  		return false, nil
   536  	}
   537  	fn := func(jobs []*perceptron.Job) (bool, error) {
   538  		return GetDropOrTruncateBlockInfoFromJobs(jobs, gcSafePoint, dom, handleJobAndBlockInfo)
   539  	}
   540  	err = admin.IterHistoryDBSJobs(txn, fn)
   541  	if err != nil {
   542  		if terror.ErrorEqual(variable.ErrSnapshotTooOld, err) {
   543  			return nil, nil, errors.Errorf("Can't find dropped/truncated causet '%s' in GC safe point %s", blockName.Name.O, perceptron.TSConvert2Time(gcSafePoint).String())
   544  		}
   545  		return nil, nil, err
   546  	}
   547  	if blockInfo == nil || jobInfo == nil {
   548  		return nil, nil, errors.Errorf("Can't find dropped/truncated causet: %v in DBS history jobs", blockName.Name)
   549  	}
   550  	return jobInfo, blockInfo, nil
   551  }
   552  
   553  func (e *DBSInterDirc) executeFlashbackBlock(s *ast.FlashBackBlockStmt) error {
   554  	job, tblInfo, err := e.getRecoverBlockByBlockName(s.Block)
   555  	if err != nil {
   556  		return err
   557  	}
   558  	if len(s.NewName) != 0 {
   559  		tblInfo.Name = perceptron.NewCIStr(s.NewName)
   560  	}
   561  	// Check the causet ID was not exists.
   562  	is := petri.GetPetri(e.ctx).SchemaReplicant()
   563  	tbl, ok := is.BlockByID(tblInfo.ID)
   564  	if ok {
   565  		return schemareplicant.ErrBlockExists.GenWithStack("Block '%-.192s' already been flashback to '%-.192s', can't be flashback repeatedly", s.Block.Name.O, tbl.Meta().Name.O)
   566  	}
   567  
   568  	autoIncID, autoRandID, err := e.getBlockAutoIDsFromSnapshot(job)
   569  	if err != nil {
   570  		return err
   571  	}
   572  	recoverInfo := &dbs.RecoverInfo{
   573  		SchemaID:      job.SchemaID,
   574  		BlockInfo:     tblInfo,
   575  		DropJobID:     job.ID,
   576  		SnapshotTS:    job.StartTS,
   577  		CurAutoIncID:  autoIncID,
   578  		CurAutoRandID: autoRandID,
   579  	}
   580  	// Call DBS RecoverBlock.
   581  	err = petri.GetPetri(e.ctx).DBS().RecoverBlock(e.ctx, recoverInfo)
   582  	return err
   583  }
   584  
   585  func (e *DBSInterDirc) executeLockBlocks(s *ast.LockBlocksStmt) error {
   586  	if !config.BlockLockEnabled() {
   587  		return nil
   588  	}
   589  	return petri.GetPetri(e.ctx).DBS().LockBlocks(e.ctx, s)
   590  }
   591  
   592  func (e *DBSInterDirc) executeUnlockBlocks(s *ast.UnlockBlocksStmt) error {
   593  	if !config.BlockLockEnabled() {
   594  		return nil
   595  	}
   596  	lockedBlocks := e.ctx.GetAllBlockLocks()
   597  	return petri.GetPetri(e.ctx).DBS().UnlockBlocks(e.ctx, lockedBlocks)
   598  }
   599  
   600  func (e *DBSInterDirc) executeCleanupBlockLock(s *ast.CleanupBlockLockStmt) error {
   601  	return petri.GetPetri(e.ctx).DBS().CleanupBlockLock(e.ctx, s.Blocks)
   602  }
   603  
   604  func (e *DBSInterDirc) executeRepairBlock(s *ast.RepairBlockStmt) error {
   605  	return petri.GetPetri(e.ctx).DBS().RepairBlock(e.ctx, s.Block, s.CreateStmt)
   606  }
   607  
   608  func (e *DBSInterDirc) executeCreateSequence(s *ast.CreateSequenceStmt) error {
   609  	return petri.GetPetri(e.ctx).DBS().CreateSequence(e.ctx, s)
   610  }