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 }