github.com/XiaoMi/Gaea@v1.2.5/parser/model/ddl.go (about)

     1  // Copyright 2015 PingCAP, 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 model
    15  
    16  import (
    17  	"encoding/json"
    18  	"fmt"
    19  	"math"
    20  	"sync"
    21  	"time"
    22  
    23  	"github.com/pingcap/errors"
    24  
    25  	"github.com/XiaoMi/Gaea/parser/terror"
    26  )
    27  
    28  // ActionType is the type for DDL action.
    29  type ActionType byte
    30  
    31  // List DDL actions.
    32  const (
    33  	ActionNone                         ActionType = 0
    34  	ActionCreateSchema                 ActionType = 1
    35  	ActionDropSchema                   ActionType = 2
    36  	ActionCreateTable                  ActionType = 3
    37  	ActionDropTable                    ActionType = 4
    38  	ActionAddColumn                    ActionType = 5
    39  	ActionDropColumn                   ActionType = 6
    40  	ActionAddIndex                     ActionType = 7
    41  	ActionDropIndex                    ActionType = 8
    42  	ActionAddForeignKey                ActionType = 9
    43  	ActionDropForeignKey               ActionType = 10
    44  	ActionTruncateTable                ActionType = 11
    45  	ActionModifyColumn                 ActionType = 12
    46  	ActionRebaseAutoID                 ActionType = 13
    47  	ActionRenameTable                  ActionType = 14
    48  	ActionSetDefaultValue              ActionType = 15
    49  	ActionShardRowID                   ActionType = 16
    50  	ActionModifyTableComment           ActionType = 17
    51  	ActionRenameIndex                  ActionType = 18
    52  	ActionAddTablePartition            ActionType = 19
    53  	ActionDropTablePartition           ActionType = 20
    54  	ActionCreateView                   ActionType = 21
    55  	ActionModifyTableCharsetAndCollate ActionType = 22
    56  	ActionTruncateTablePartition       ActionType = 23
    57  	ActionDropView                     ActionType = 24
    58  	ActionRestoreTable                 ActionType = 25
    59  )
    60  
    61  // AddIndexStr is a string related to the operation of "add index".
    62  const AddIndexStr = "add index"
    63  
    64  var actionMap = map[ActionType]string{
    65  	ActionCreateSchema:                 "create schema",
    66  	ActionDropSchema:                   "drop schema",
    67  	ActionCreateTable:                  "create table",
    68  	ActionDropTable:                    "drop table",
    69  	ActionAddColumn:                    "add column",
    70  	ActionDropColumn:                   "drop column",
    71  	ActionAddIndex:                     AddIndexStr,
    72  	ActionDropIndex:                    "drop index",
    73  	ActionAddForeignKey:                "add foreign key",
    74  	ActionDropForeignKey:               "drop foreign key",
    75  	ActionTruncateTable:                "truncate table",
    76  	ActionModifyColumn:                 "modify column",
    77  	ActionRebaseAutoID:                 "rebase auto_increment ID",
    78  	ActionRenameTable:                  "rename table",
    79  	ActionSetDefaultValue:              "set default value",
    80  	ActionShardRowID:                   "shard row ID",
    81  	ActionModifyTableComment:           "modify table comment",
    82  	ActionRenameIndex:                  "rename index",
    83  	ActionAddTablePartition:            "add partition",
    84  	ActionDropTablePartition:           "drop partition",
    85  	ActionCreateView:                   "create view",
    86  	ActionModifyTableCharsetAndCollate: "modify table charset and collate",
    87  	ActionTruncateTablePartition:       "truncate partition",
    88  	ActionDropView:                     "drop view",
    89  	ActionRestoreTable:                 "restore table",
    90  }
    91  
    92  // String return current ddl action in string
    93  func (action ActionType) String() string {
    94  	if v, ok := actionMap[action]; ok {
    95  		return v
    96  	}
    97  	return "none"
    98  }
    99  
   100  // HistoryInfo is used for binlog.
   101  type HistoryInfo struct {
   102  	SchemaVersion int64
   103  	DBInfo        *DBInfo
   104  	TableInfo     *TableInfo
   105  	FinishedTS    uint64
   106  }
   107  
   108  // AddDBInfo adds schema version and schema information that are used for binlog.
   109  // dbInfo is added in the following operations: create database, drop database.
   110  func (h *HistoryInfo) AddDBInfo(schemaVer int64, dbInfo *DBInfo) {
   111  	h.SchemaVersion = schemaVer
   112  	h.DBInfo = dbInfo
   113  }
   114  
   115  // AddTableInfo adds schema version and table information that are used for binlog.
   116  // tblInfo is added except for the following operations: create database, drop database.
   117  func (h *HistoryInfo) AddTableInfo(schemaVer int64, tblInfo *TableInfo) {
   118  	h.SchemaVersion = schemaVer
   119  	h.TableInfo = tblInfo
   120  }
   121  
   122  // Clean cleans history information.
   123  func (h *HistoryInfo) Clean() {
   124  	h.SchemaVersion = 0
   125  	h.DBInfo = nil
   126  	h.TableInfo = nil
   127  }
   128  
   129  // DDLReorgMeta is meta info of DDL reorganization.
   130  type DDLReorgMeta struct {
   131  	// EndHandle is the last handle of the adding indices table.
   132  	// We should only backfill indices in the range [startHandle, EndHandle].
   133  	EndHandle int64 `json:"end_handle"`
   134  }
   135  
   136  // NewDDLReorgMeta new a DDLReorgMeta.
   137  func NewDDLReorgMeta() *DDLReorgMeta {
   138  	return &DDLReorgMeta{
   139  		EndHandle: math.MaxInt64,
   140  	}
   141  }
   142  
   143  // Job is for a DDL operation.
   144  type Job struct {
   145  	ID       int64         `json:"id"`
   146  	Type     ActionType    `json:"type"`
   147  	SchemaID int64         `json:"schema_id"`
   148  	TableID  int64         `json:"table_id"`
   149  	State    JobState      `json:"state"`
   150  	Error    *terror.Error `json:"err"`
   151  	// ErrorCount will be increased, every time we meet an error when running job.
   152  	ErrorCount int64 `json:"err_count"`
   153  	// RowCount means the number of rows that are processed.
   154  	RowCount int64         `json:"row_count"`
   155  	Mu       sync.Mutex    `json:"-"`
   156  	Args     []interface{} `json:"-"`
   157  	// RawArgs : We must use json raw message to delay parsing special args.
   158  	RawArgs     json.RawMessage `json:"raw_args"`
   159  	SchemaState SchemaState     `json:"schema_state"`
   160  	// SnapshotVer means snapshot version for this job.
   161  	SnapshotVer uint64 `json:"snapshot_ver"`
   162  	// StartTS uses timestamp allocated by TSO.
   163  	// Now it's the TS when we put the job to TiKV queue.
   164  	StartTS uint64 `json:"start_ts"`
   165  	// DependencyID is the job's ID that the current job depends on.
   166  	DependencyID int64 `json:"dependency_id"`
   167  	// Query string of the ddl job.
   168  	Query      string       `json:"query"`
   169  	BinlogInfo *HistoryInfo `json:"binlog"`
   170  
   171  	// Version indicates the DDL job version. For old jobs, it will be 0.
   172  	Version int64 `json:"version"`
   173  
   174  	// ReorgMeta is meta info of ddl reorganization.
   175  	// This field is depreciated.
   176  	ReorgMeta *DDLReorgMeta `json:"reorg_meta"`
   177  
   178  	// Priority is only used to set the operation priority of adding indices.
   179  	Priority int `json:"priority"`
   180  }
   181  
   182  // FinishTableJob is called when a job is finished.
   183  // It updates the job's state information and adds tblInfo to the binlog.
   184  func (job *Job) FinishTableJob(jobState JobState, schemaState SchemaState, ver int64, tblInfo *TableInfo) {
   185  	job.State = jobState
   186  	job.SchemaState = schemaState
   187  	job.BinlogInfo.AddTableInfo(ver, tblInfo)
   188  }
   189  
   190  // FinishDBJob is called when a job is finished.
   191  // It updates the job's state information and adds dbInfo the binlog.
   192  func (job *Job) FinishDBJob(jobState JobState, schemaState SchemaState, ver int64, dbInfo *DBInfo) {
   193  	job.State = jobState
   194  	job.SchemaState = schemaState
   195  	job.BinlogInfo.AddDBInfo(ver, dbInfo)
   196  }
   197  
   198  // TSConvert2Time converts timestamp to time.
   199  func TSConvert2Time(ts uint64) time.Time {
   200  	t := int64(ts >> 18) // 18 is for the logical time.
   201  	return time.Unix(t/1e3, (t%1e3)*1e6)
   202  }
   203  
   204  // SetRowCount sets the number of rows. Make sure it can pass `make race`.
   205  func (job *Job) SetRowCount(count int64) {
   206  	job.Mu.Lock()
   207  	defer job.Mu.Unlock()
   208  
   209  	job.RowCount = count
   210  }
   211  
   212  // GetRowCount gets the number of rows. Make sure it can pass `make race`.
   213  func (job *Job) GetRowCount() int64 {
   214  	job.Mu.Lock()
   215  	defer job.Mu.Unlock()
   216  
   217  	return job.RowCount
   218  }
   219  
   220  // Encode encodes job with json format.
   221  // updateRawArgs is used to determine whether to update the raw args.
   222  func (job *Job) Encode(updateRawArgs bool) ([]byte, error) {
   223  	var err error
   224  	if updateRawArgs {
   225  		job.RawArgs, err = json.Marshal(job.Args)
   226  		if err != nil {
   227  			return nil, errors.Trace(err)
   228  		}
   229  	}
   230  
   231  	var b []byte
   232  	job.Mu.Lock()
   233  	defer job.Mu.Unlock()
   234  	b, err = json.Marshal(job)
   235  
   236  	return b, errors.Trace(err)
   237  }
   238  
   239  // Decode decodes job from the json buffer, we must use DecodeArgs later to
   240  // decode special args for this job.
   241  func (job *Job) Decode(b []byte) error {
   242  	err := json.Unmarshal(b, job)
   243  	return errors.Trace(err)
   244  }
   245  
   246  // DecodeArgs decodes job args.
   247  func (job *Job) DecodeArgs(args ...interface{}) error {
   248  	job.Args = args
   249  	err := json.Unmarshal(job.RawArgs, &job.Args)
   250  	return errors.Trace(err)
   251  }
   252  
   253  // String implements fmt.Stringer interface.
   254  func (job *Job) String() string {
   255  	rowCount := job.GetRowCount()
   256  	return fmt.Sprintf("ID:%d, Type:%s, State:%s, SchemaState:%s, SchemaID:%d, TableID:%d, RowCount:%d, ArgLen:%d, start time: %v, Err:%v, ErrCount:%d, SnapshotVersion:%v",
   257  		job.ID, job.Type, job.State, job.SchemaState, job.SchemaID, job.TableID, rowCount, len(job.Args), TSConvert2Time(job.StartTS), job.Error, job.ErrorCount, job.SnapshotVer)
   258  }
   259  
   260  func (job *Job) hasDependentSchema(other *Job) (bool, error) {
   261  	if other.Type == ActionDropSchema || other.Type == ActionCreateSchema {
   262  		if other.SchemaID == job.SchemaID {
   263  			return true, nil
   264  		}
   265  		if job.Type == ActionRenameTable {
   266  			var oldSchemaID int64
   267  			if err := job.DecodeArgs(&oldSchemaID); err != nil {
   268  				return false, errors.Trace(err)
   269  			}
   270  			if other.SchemaID == oldSchemaID {
   271  				return true, nil
   272  			}
   273  		}
   274  	}
   275  	return false, nil
   276  }
   277  
   278  // IsDependentOn returns whether the job depends on "other".
   279  // How to check the job depends on "other"?
   280  // 1. The two jobs handle the same database when one of the two jobs is an ActionDropSchema or ActionCreateSchema type.
   281  // 2. Or the two jobs handle the same table.
   282  func (job *Job) IsDependentOn(other *Job) (bool, error) {
   283  	isDependent, err := job.hasDependentSchema(other)
   284  	if err != nil || isDependent {
   285  		return isDependent, errors.Trace(err)
   286  	}
   287  	isDependent, err = other.hasDependentSchema(job)
   288  	if err != nil || isDependent {
   289  		return isDependent, errors.Trace(err)
   290  	}
   291  
   292  	// TODO: If a job is ActionRenameTable, we need to check table name.
   293  	if other.TableID == job.TableID {
   294  		return true, nil
   295  	}
   296  	return false, nil
   297  }
   298  
   299  // IsFinished returns whether job is finished or not.
   300  // If the job state is Done or Cancelled, it is finished.
   301  func (job *Job) IsFinished() bool {
   302  	return job.State == JobStateDone || job.State == JobStateRollbackDone || job.State == JobStateCancelled
   303  }
   304  
   305  // IsCancelled returns whether the job is cancelled or not.
   306  func (job *Job) IsCancelled() bool {
   307  	return job.State == JobStateCancelled
   308  }
   309  
   310  // IsRollbackDone returns whether the job is rolled back or not.
   311  func (job *Job) IsRollbackDone() bool {
   312  	return job.State == JobStateRollbackDone
   313  }
   314  
   315  // IsRollingback returns whether the job is rolling back or not.
   316  func (job *Job) IsRollingback() bool {
   317  	return job.State == JobStateRollingback
   318  }
   319  
   320  // IsCancelling returns whether the job is cancelling or not.
   321  func (job *Job) IsCancelling() bool {
   322  	return job.State == JobStateCancelling
   323  }
   324  
   325  // IsSynced returns whether the DDL modification is synced among all TiDB servers.
   326  func (job *Job) IsSynced() bool {
   327  	return job.State == JobStateSynced
   328  }
   329  
   330  // IsDone returns whether job is done.
   331  func (job *Job) IsDone() bool {
   332  	return job.State == JobStateDone
   333  }
   334  
   335  // IsRunning returns whether job is still running or not.
   336  func (job *Job) IsRunning() bool {
   337  	return job.State == JobStateRunning
   338  }
   339  
   340  // JobState is for job state.
   341  type JobState byte
   342  
   343  // List job states.
   344  const (
   345  	JobStateNone    JobState = 0
   346  	JobStateRunning JobState = 1
   347  	// When DDL encountered an unrecoverable error at reorganization state,
   348  	// some keys has been added already, we need to remove them.
   349  	// JobStateRollingback is the state to do the rolling back job.
   350  	JobStateRollingback  JobState = 2
   351  	JobStateRollbackDone JobState = 3
   352  	JobStateDone         JobState = 4
   353  	JobStateCancelled    JobState = 5
   354  	// JobStateSynced is used to mark the information about the completion of this job
   355  	// has been synchronized to all servers.
   356  	JobStateSynced JobState = 6
   357  	// JobStateCancelling is used to mark the DDL job is cancelled by the client, but the DDL work hasn't handle it.
   358  	JobStateCancelling JobState = 7
   359  )
   360  
   361  // String implements fmt.Stringer interface.
   362  func (s JobState) String() string {
   363  	switch s {
   364  	case JobStateRunning:
   365  		return "running"
   366  	case JobStateRollingback:
   367  		return "rollingback"
   368  	case JobStateRollbackDone:
   369  		return "rollback done"
   370  	case JobStateDone:
   371  		return "done"
   372  	case JobStateCancelled:
   373  		return "cancelled"
   374  	case JobStateCancelling:
   375  		return "cancelling"
   376  	case JobStateSynced:
   377  		return "synced"
   378  	default:
   379  		return "none"
   380  	}
   381  }
   382  
   383  // SchemaDiff contains the schema modification at a particular schema version.
   384  // It is used to reduce schema reload cost.
   385  type SchemaDiff struct {
   386  	Version  int64      `json:"version"`
   387  	Type     ActionType `json:"type"`
   388  	SchemaID int64      `json:"schema_id"`
   389  	TableID  int64      `json:"table_id"`
   390  
   391  	// OldTableID is the table ID before truncate, only used by truncate table DDL.
   392  	OldTableID int64 `json:"old_table_id"`
   393  	// OldSchemaID is the schema ID before rename table, only used by rename table DDL.
   394  	OldSchemaID int64 `json:"old_schema_id"`
   395  }