github.com/whtcorpsinc/MilevaDB-Prod@v0.0.0-20211104133533-f57f4be3b597/dbs/dbs_api.go (about) 1 // Copyright 2020 The ql Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSES/QL-LICENSE file. 4 5 // Copyright 2020 WHTCORPS INC, Inc. 6 // 7 // Licensed under the Apache License, Version 2.0 (the "License"); 8 // you may not use this file except in compliance with the License. 9 // You may obtain a copy of the License at 10 // 11 // http://www.apache.org/licenses/LICENSE-2.0 12 // 13 // Unless required by applicable law or agreed to in writing, software 14 // distributed under the License is distributed on an "AS IS" BASIS, 15 // See the License for the specific language governing permissions and 16 // limitations under the License. 17 18 package dbs 19 20 import ( 21 "bytes" 22 "encoding/hex" 23 "encoding/json" 24 "fmt" 25 "math" 26 "strconv" 27 "strings" 28 "sync/atomic" 29 "time" 30 31 "github.com/cznic/mathutil" 32 "github.com/whtcorpsinc/BerolinaSQL/allegrosql" 33 "github.com/whtcorpsinc/BerolinaSQL/ast" 34 "github.com/whtcorpsinc/BerolinaSQL/charset" 35 "github.com/whtcorpsinc/BerolinaSQL/format" 36 "github.com/whtcorpsinc/BerolinaSQL/perceptron" 37 field_types "github.com/whtcorpsinc/BerolinaSQL/types" 38 "github.com/whtcorpsinc/errors" 39 "github.com/whtcorpsinc/milevadb/blockcodec" 40 "github.com/whtcorpsinc/milevadb/causet" 41 "github.com/whtcorpsinc/milevadb/causet/blocks" 42 "github.com/whtcorpsinc/milevadb/config" 43 "github.com/whtcorpsinc/milevadb/dbs/memristed" 44 "github.com/whtcorpsinc/milevadb/ekv" 45 "github.com/whtcorpsinc/milevadb/memex" 46 "github.com/whtcorpsinc/milevadb/schemareplicant" 47 "github.com/whtcorpsinc/milevadb/soliton" 48 "github.com/whtcorpsinc/milevadb/soliton/chunk" 49 "github.com/whtcorpsinc/milevadb/soliton/codec" 50 "github.com/whtcorpsinc/milevadb/soliton/defCauslate" 51 "github.com/whtcorpsinc/milevadb/soliton/logutil" 52 "github.com/whtcorpsinc/milevadb/soliton/mock" 53 "github.com/whtcorpsinc/milevadb/soliton/petriutil" 54 "github.com/whtcorpsinc/milevadb/soliton/set" 55 "github.com/whtcorpsinc/milevadb/spacetime/autoid" 56 "github.com/whtcorpsinc/milevadb/stochastikctx" 57 "github.com/whtcorpsinc/milevadb/stochastikctx/variable" 58 "github.com/whtcorpsinc/milevadb/types" 59 driver "github.com/whtcorpsinc/milevadb/types/BerolinaSQL_driver" 60 "go.uber.org/zap" 61 ) 62 63 const ( 64 memexIndexPrefix = "_V$" 65 changingDeferredCausetPrefix = "_DefCaus$_" 66 changingIndexPrefix = "_Idx$_" 67 ) 68 69 func (d *dbs) CreateSchema(ctx stochastikctx.Context, schemaReplicant perceptron.CIStr, charsetInfo *ast.CharsetOpt) error { 70 dbInfo := &perceptron.DBInfo{Name: schemaReplicant} 71 if charsetInfo != nil { 72 chs, defCausl, err := ResolveCharsetDefCauslation(ast.CharsetOpt{Chs: charsetInfo.Chs, DefCaus: charsetInfo.DefCaus}) 73 if err != nil { 74 return errors.Trace(err) 75 } 76 dbInfo.Charset = chs 77 dbInfo.DefCauslate = defCausl 78 } else { 79 dbInfo.Charset, dbInfo.DefCauslate = charset.GetDefaultCharsetAndDefCauslate() 80 } 81 return d.CreateSchemaWithInfo(ctx, dbInfo, OnExistError, false /*tryRetainID*/) 82 } 83 84 func (d *dbs) CreateSchemaWithInfo( 85 ctx stochastikctx.Context, 86 dbInfo *perceptron.DBInfo, 87 onExist OnExist, 88 tryRetainID bool, 89 ) error { 90 is := d.GetSchemaReplicantWithInterceptor(ctx) 91 _, ok := is.SchemaByName(dbInfo.Name) 92 if ok { 93 err := schemareplicant.ErrDatabaseExists.GenWithStackByArgs(dbInfo.Name) 94 switch onExist { 95 case OnExistIgnore: 96 ctx.GetStochastikVars().StmtCtx.AppendNote(err) 97 return nil 98 case OnExistError, OnExistReplace: 99 // FIXME: can we implement MariaDB's CREATE OR REPLACE SCHEMA? 100 return err 101 } 102 } 103 104 if err := checkTooLongSchema(dbInfo.Name); err != nil { 105 return errors.Trace(err) 106 } 107 108 if err := checkCharsetAndDefCauslation(dbInfo.Charset, dbInfo.DefCauslate); err != nil { 109 return errors.Trace(err) 110 } 111 112 // FIXME: support `tryRetainID`. 113 genIDs, err := d.genGlobalIDs(1) 114 if err != nil { 115 return errors.Trace(err) 116 } 117 dbInfo.ID = genIDs[0] 118 119 job := &perceptron.Job{ 120 SchemaID: dbInfo.ID, 121 SchemaName: dbInfo.Name.L, 122 Type: perceptron.CausetActionCreateSchema, 123 BinlogInfo: &perceptron.HistoryInfo{}, 124 Args: []interface{}{dbInfo}, 125 } 126 127 err = d.doDBSJob(ctx, job) 128 err = d.callHookOnChanged(err) 129 return errors.Trace(err) 130 } 131 132 func (d *dbs) AlterSchema(ctx stochastikctx.Context, stmt *ast.AlterDatabaseStmt) (err error) { 133 // Resolve target charset and defCauslation from options. 134 var toCharset, toDefCauslate string 135 for _, val := range stmt.Options { 136 switch val.Tp { 137 case ast.DatabaseOptionCharset: 138 if toCharset == "" { 139 toCharset = val.Value 140 } else if toCharset != val.Value { 141 return ErrConflictingDeclarations.GenWithStackByArgs(toCharset, val.Value) 142 } 143 case ast.DatabaseOptionDefCauslate: 144 info, err := defCauslate.GetDefCauslationByName(val.Value) 145 if err != nil { 146 return errors.Trace(err) 147 } 148 if toCharset == "" { 149 toCharset = info.CharsetName 150 } else if toCharset != info.CharsetName { 151 return ErrConflictingDeclarations.GenWithStackByArgs(toCharset, info.CharsetName) 152 } 153 toDefCauslate = info.Name 154 } 155 } 156 if toDefCauslate == "" { 157 if toDefCauslate, err = charset.GetDefaultDefCauslation(toCharset); err != nil { 158 return errors.Trace(err) 159 } 160 } 161 162 // Check if need to change charset/defCauslation. 163 dbName := perceptron.NewCIStr(stmt.Name) 164 is := d.GetSchemaReplicantWithInterceptor(ctx) 165 dbInfo, ok := is.SchemaByName(dbName) 166 if !ok { 167 return schemareplicant.ErrDatabaseNotExists.GenWithStackByArgs(dbName.O) 168 } 169 if dbInfo.Charset == toCharset && dbInfo.DefCauslate == toDefCauslate { 170 return nil 171 } 172 173 // Do the DBS job. 174 job := &perceptron.Job{ 175 SchemaID: dbInfo.ID, 176 SchemaName: dbInfo.Name.L, 177 Type: perceptron.CausetActionModifySchemaCharsetAndDefCauslate, 178 BinlogInfo: &perceptron.HistoryInfo{}, 179 Args: []interface{}{toCharset, toDefCauslate}, 180 } 181 err = d.doDBSJob(ctx, job) 182 err = d.callHookOnChanged(err) 183 return errors.Trace(err) 184 } 185 186 func (d *dbs) DropSchema(ctx stochastikctx.Context, schemaReplicant perceptron.CIStr) (err error) { 187 is := d.GetSchemaReplicantWithInterceptor(ctx) 188 old, ok := is.SchemaByName(schemaReplicant) 189 if !ok { 190 return errors.Trace(schemareplicant.ErrDatabaseNotExists) 191 } 192 job := &perceptron.Job{ 193 SchemaID: old.ID, 194 SchemaName: old.Name.L, 195 Type: perceptron.CausetActionDropSchema, 196 BinlogInfo: &perceptron.HistoryInfo{}, 197 } 198 199 err = d.doDBSJob(ctx, job) 200 err = d.callHookOnChanged(err) 201 if err != nil { 202 return errors.Trace(err) 203 } 204 if !config.TableLockEnabled() { 205 return nil 206 } 207 // Clear causet locks hold by the stochastik. 208 tbs := is.SchemaTables(schemaReplicant) 209 lockTableIDs := make([]int64, 0) 210 for _, tb := range tbs { 211 if ok, _ := ctx.CheckTableLocked(tb.Meta().ID); ok { 212 lockTableIDs = append(lockTableIDs, tb.Meta().ID) 213 } 214 } 215 ctx.ReleaseTableLockByTableIDs(lockTableIDs) 216 return nil 217 } 218 219 func checkTooLongSchema(schemaReplicant perceptron.CIStr) error { 220 if len(schemaReplicant.L) > allegrosql.MaxDatabaseNameLength { 221 return ErrTooLongIdent.GenWithStackByArgs(schemaReplicant) 222 } 223 return nil 224 } 225 226 func checkTooLongTable(causet perceptron.CIStr) error { 227 if len(causet.L) > allegrosql.MaxTableNameLength { 228 return ErrTooLongIdent.GenWithStackByArgs(causet) 229 } 230 return nil 231 } 232 233 func checkTooLongIndex(index perceptron.CIStr) error { 234 if len(index.L) > allegrosql.MaxIndexIdentifierLen { 235 return ErrTooLongIdent.GenWithStackByArgs(index) 236 } 237 return nil 238 } 239 240 func setDeferredCausetFlagWithConstraint(defCausMap map[string]*causet.DeferredCauset, v *ast.Constraint) { 241 switch v.Tp { 242 case ast.ConstraintPrimaryKey: 243 for _, key := range v.Keys { 244 if key.Expr != nil { 245 continue 246 } 247 c, ok := defCausMap[key.DeferredCauset.Name.L] 248 if !ok { 249 continue 250 } 251 c.Flag |= allegrosql.PriKeyFlag 252 // Primary key can not be NULL. 253 c.Flag |= allegrosql.NotNullFlag 254 } 255 case ast.ConstraintUniq, ast.ConstraintUniqIndex, ast.ConstraintUniqKey: 256 for i, key := range v.Keys { 257 if key.Expr != nil { 258 continue 259 } 260 c, ok := defCausMap[key.DeferredCauset.Name.L] 261 if !ok { 262 continue 263 } 264 if i == 0 { 265 // Only the first defCausumn can be set 266 // if unique index has multi defCausumns, 267 // the flag should be MultipleKeyFlag. 268 // See https://dev.allegrosql.com/doc/refman/5.7/en/show-defCausumns.html 269 if len(v.Keys) > 1 { 270 c.Flag |= allegrosql.MultipleKeyFlag 271 } else { 272 c.Flag |= allegrosql.UniqueKeyFlag 273 } 274 } 275 } 276 case ast.ConstraintKey, ast.ConstraintIndex: 277 for i, key := range v.Keys { 278 if key.Expr != nil { 279 continue 280 } 281 c, ok := defCausMap[key.DeferredCauset.Name.L] 282 if !ok { 283 continue 284 } 285 if i == 0 { 286 // Only the first defCausumn can be set. 287 c.Flag |= allegrosql.MultipleKeyFlag 288 } 289 } 290 } 291 } 292 293 func buildDeferredCausetsAndConstraints( 294 ctx stochastikctx.Context, 295 defCausDefs []*ast.DeferredCausetDef, 296 constraints []*ast.Constraint, 297 tblCharset string, 298 tblDefCauslate string, 299 ) ([]*causet.DeferredCauset, []*ast.Constraint, error) { 300 defCausMap := map[string]*causet.DeferredCauset{} 301 // outPriKeyConstraint is the primary key constraint out of defCausumn definition. such as: create causet t1 (id int , age int, primary key(id)); 302 var outPriKeyConstraint *ast.Constraint 303 for _, v := range constraints { 304 if v.Tp == ast.ConstraintPrimaryKey { 305 outPriKeyConstraint = v 306 break 307 } 308 } 309 defcaus := make([]*causet.DeferredCauset, 0, len(defCausDefs)) 310 for i, defCausDef := range defCausDefs { 311 defCaus, cts, err := buildDeferredCausetAndConstraint(ctx, i, defCausDef, outPriKeyConstraint, tblCharset, tblDefCauslate) 312 if err != nil { 313 return nil, nil, errors.Trace(err) 314 } 315 defCaus.State = perceptron.StatePublic 316 constraints = append(constraints, cts...) 317 defcaus = append(defcaus, defCaus) 318 defCausMap[defCausDef.Name.Name.L] = defCaus 319 } 320 // Traverse causet Constraints and set defCaus.flag. 321 for _, v := range constraints { 322 setDeferredCausetFlagWithConstraint(defCausMap, v) 323 } 324 return defcaus, constraints, nil 325 } 326 327 // ResolveCharsetDefCauslation will resolve the charset and defCauslate by the order of parameters: 328 // * If any given ast.CharsetOpt is not empty, the resolved charset and defCauslate will be returned. 329 // * If all ast.CharsetOpts are empty, the default charset and defCauslate will be returned. 330 func ResolveCharsetDefCauslation(charsetOpts ...ast.CharsetOpt) (string, string, error) { 331 for _, v := range charsetOpts { 332 if v.DefCaus != "" { 333 defCauslation, err := defCauslate.GetDefCauslationByName(v.DefCaus) 334 if err != nil { 335 return "", "", errors.Trace(err) 336 } 337 if v.Chs != "" && defCauslation.CharsetName != v.Chs { 338 return "", "", charset.ErrDefCauslationCharsetMismatch.GenWithStackByArgs(v.DefCaus, v.Chs) 339 } 340 return defCauslation.CharsetName, v.DefCaus, nil 341 } 342 if v.Chs != "" { 343 defCausl, err := charset.GetDefaultDefCauslation(v.Chs) 344 if err != nil { 345 return "", "", errors.Trace(err) 346 } 347 return v.Chs, defCausl, err 348 } 349 } 350 chs, defCausl := charset.GetDefaultCharsetAndDefCauslate() 351 return chs, defCausl, nil 352 } 353 354 func typesNeedCharset(tp byte) bool { 355 switch tp { 356 case allegrosql.TypeString, allegrosql.TypeVarchar, allegrosql.TypeVarString, 357 allegrosql.TypeBlob, allegrosql.TypeTinyBlob, allegrosql.TypeMediumBlob, allegrosql.TypeLongBlob, 358 allegrosql.TypeEnum, allegrosql.TypeSet: 359 return true 360 } 361 return false 362 } 363 364 func setCharsetDefCauslationFlenDecimal(tp *types.FieldType, defCausCharset, defCausDefCauslate string) error { 365 if typesNeedCharset(tp.Tp) { 366 tp.Charset = defCausCharset 367 tp.DefCauslate = defCausDefCauslate 368 } else { 369 tp.Charset = charset.CharsetBin 370 tp.DefCauslate = charset.CharsetBin 371 } 372 373 // Use default value for flen or decimal when they are unspecified. 374 defaultFlen, defaultDecimal := allegrosql.GetDefaultFieldLengthAndDecimal(tp.Tp) 375 if tp.Flen == types.UnspecifiedLength { 376 tp.Flen = defaultFlen 377 if allegrosql.HasUnsignedFlag(tp.Flag) && tp.Tp != allegrosql.TypeLonglong && allegrosql.IsIntegerType(tp.Tp) { 378 // Issue #4684: the flen of unsigned integer(except bigint) is 1 digit shorter than signed integer 379 // because it has no prefix "+" or "-" character. 380 tp.Flen-- 381 } 382 } 383 if tp.Decimal == types.UnspecifiedLength { 384 tp.Decimal = defaultDecimal 385 } 386 return nil 387 } 388 389 // buildDeferredCausetAndConstraint builds causet.DeferredCauset and ast.Constraint from the parameters. 390 // outPriKeyConstraint is the primary key constraint out of defCausumn definition. For example: 391 // `create causet t1 (id int , age int, primary key(id));` 392 func buildDeferredCausetAndConstraint( 393 ctx stochastikctx.Context, 394 offset int, 395 defCausDef *ast.DeferredCausetDef, 396 outPriKeyConstraint *ast.Constraint, 397 tblCharset string, 398 tblDefCauslate string, 399 ) (*causet.DeferredCauset, []*ast.Constraint, error) { 400 if defCausName := defCausDef.Name.Name.L; defCausName == perceptron.ExtraHandleName.L { 401 return nil, nil, ErrWrongDeferredCausetName.GenWithStackByArgs(defCausName) 402 } 403 404 // specifiedDefCauslate refers to the last defCauslate specified in defCausDef.Options. 405 chs, defCausl, err := getCharsetAndDefCauslateInDeferredCausetDef(defCausDef) 406 if err != nil { 407 return nil, nil, errors.Trace(err) 408 } 409 chs, defCausl, err = ResolveCharsetDefCauslation( 410 ast.CharsetOpt{Chs: chs, DefCaus: defCausl}, 411 ast.CharsetOpt{Chs: tblCharset, DefCaus: tblDefCauslate}, 412 ) 413 if err != nil { 414 return nil, nil, errors.Trace(err) 415 } 416 417 if err := setCharsetDefCauslationFlenDecimal(defCausDef.Tp, chs, defCausl); err != nil { 418 return nil, nil, errors.Trace(err) 419 } 420 defCaus, cts, err := defCausumnDefToDefCaus(ctx, offset, defCausDef, outPriKeyConstraint) 421 if err != nil { 422 return nil, nil, errors.Trace(err) 423 } 424 return defCaus, cts, nil 425 } 426 427 // checkDeferredCausetDefaultValue checks the default value of the defCausumn. 428 // In non-strict ALLEGROALLEGROSQL mode, if the default value of the defCausumn is an empty string, the default value can be ignored. 429 // In strict ALLEGROALLEGROSQL mode, TEXT/BLOB/JSON can't have not null default values. 430 // In NO_ZERO_DATE ALLEGROALLEGROSQL mode, TIMESTAMP/DATE/DATETIME type can't have zero date like '0000-00-00' or '0000-00-00 00:00:00'. 431 func checkDeferredCausetDefaultValue(ctx stochastikctx.Context, defCaus *causet.DeferredCauset, value interface{}) (bool, interface{}, error) { 432 hasDefaultValue := true 433 if value != nil && (defCaus.Tp == allegrosql.TypeJSON || 434 defCaus.Tp == allegrosql.TypeTinyBlob || defCaus.Tp == allegrosql.TypeMediumBlob || 435 defCaus.Tp == allegrosql.TypeLongBlob || defCaus.Tp == allegrosql.TypeBlob) { 436 // In non-strict ALLEGROALLEGROSQL mode. 437 if !ctx.GetStochastikVars().ALLEGROSQLMode.HasStrictMode() && value == "" { 438 if defCaus.Tp == allegrosql.TypeBlob || defCaus.Tp == allegrosql.TypeLongBlob { 439 // The TEXT/BLOB default value can be ignored. 440 hasDefaultValue = false 441 } 442 // In non-strict ALLEGROALLEGROSQL mode, if the defCausumn type is json and the default value is null, it is initialized to an empty array. 443 if defCaus.Tp == allegrosql.TypeJSON { 444 value = `null` 445 } 446 sc := ctx.GetStochastikVars().StmtCtx 447 sc.AppendWarning(errBlobCantHaveDefault.GenWithStackByArgs(defCaus.Name.O)) 448 return hasDefaultValue, value, nil 449 } 450 // In strict ALLEGROALLEGROSQL mode or default value is not an empty string. 451 return hasDefaultValue, value, errBlobCantHaveDefault.GenWithStackByArgs(defCaus.Name.O) 452 } 453 if value != nil && ctx.GetStochastikVars().ALLEGROSQLMode.HasNoZeroDateMode() && 454 ctx.GetStochastikVars().ALLEGROSQLMode.HasStrictMode() && types.IsTypeTime(defCaus.Tp) { 455 if vv, ok := value.(string); ok { 456 timeValue, err := memex.GetTimeValue(ctx, vv, defCaus.Tp, int8(defCaus.Decimal)) 457 if err != nil { 458 return hasDefaultValue, value, errors.Trace(err) 459 } 460 if timeValue.GetMysqlTime().CoreTime() == types.ZeroCoreTime { 461 return hasDefaultValue, value, types.ErrInvalidDefault.GenWithStackByArgs(defCaus.Name.O) 462 } 463 } 464 } 465 return hasDefaultValue, value, nil 466 } 467 468 func checkSequenceDefaultValue(defCaus *causet.DeferredCauset) error { 469 if allegrosql.IsIntegerType(defCaus.Tp) { 470 return nil 471 } 472 return ErrDeferredCausetTypeUnsupportedNextValue.GenWithStackByArgs(defCaus.DeferredCausetInfo.Name.O) 473 } 474 475 func convertTimestamFIDelefaultValToUTC(ctx stochastikctx.Context, defaultVal interface{}, defCaus *causet.DeferredCauset) (interface{}, error) { 476 if defaultVal == nil || defCaus.Tp != allegrosql.TypeTimestamp { 477 return defaultVal, nil 478 } 479 if vv, ok := defaultVal.(string); ok { 480 if vv != types.ZeroDatetimeStr && !strings.EqualFold(vv, ast.CurrentTimestamp) { 481 t, err := types.ParseTime(ctx.GetStochastikVars().StmtCtx, vv, defCaus.Tp, int8(defCaus.Decimal)) 482 if err != nil { 483 return defaultVal, errors.Trace(err) 484 } 485 err = t.ConvertTimeZone(ctx.GetStochastikVars().Location(), time.UTC) 486 if err != nil { 487 return defaultVal, errors.Trace(err) 488 } 489 defaultVal = t.String() 490 } 491 } 492 return defaultVal, nil 493 } 494 495 // isExplicitTimeStamp is used to check if explicit_defaults_for_timestamp is on or off. 496 // Check out this link for more details. 497 // https://dev.allegrosql.com/doc/refman/5.7/en/server-system-variables.html#sysvar_explicit_defaults_for_timestamp 498 func isExplicitTimeStamp() bool { 499 // TODO: implement the behavior as MyALLEGROSQL when explicit_defaults_for_timestamp = off, then this function could return false. 500 return true 501 } 502 503 // processDeferredCausetFlags is used by defCausumnDefToDefCaus and processDeferredCausetOptions. It is intended to unify behaviors on `create/add` and `modify/change` memexs. Check milevadb#issue#19342. 504 func processDeferredCausetFlags(defCaus *causet.DeferredCauset) { 505 if defCaus.FieldType.EvalType().IsStringHoTT() && defCaus.Charset == charset.CharsetBin { 506 defCaus.Flag |= allegrosql.BinaryFlag 507 } 508 if defCaus.Tp == allegrosql.TypeBit { 509 // For BIT field, it's charset is binary but does not have binary flag. 510 defCaus.Flag &= ^allegrosql.BinaryFlag 511 defCaus.Flag |= allegrosql.UnsignedFlag 512 } 513 if defCaus.Tp == allegrosql.TypeYear { 514 // For Year field, it's charset is binary but does not have binary flag. 515 defCaus.Flag &= ^allegrosql.BinaryFlag 516 defCaus.Flag |= allegrosql.ZerofillFlag 517 } 518 519 // If you specify ZEROFILL for a numeric defCausumn, MyALLEGROSQL automatically adds the UNSIGNED attribute to the defCausumn. 520 // See https://dev.allegrosql.com/doc/refman/5.7/en/numeric-type-overview.html for more details. 521 // But some types like bit and year, won't show its unsigned flag in `show create causet`. 522 if allegrosql.HasZerofillFlag(defCaus.Flag) { 523 defCaus.Flag |= allegrosql.UnsignedFlag 524 } 525 } 526 527 // defCausumnDefToDefCaus converts DeferredCausetDef to DefCaus and TableConstraints. 528 // outPriKeyConstraint is the primary key constraint out of defCausumn definition. such as: create causet t1 (id int , age int, primary key(id)); 529 func defCausumnDefToDefCaus(ctx stochastikctx.Context, offset int, defCausDef *ast.DeferredCausetDef, outPriKeyConstraint *ast.Constraint) (*causet.DeferredCauset, []*ast.Constraint, error) { 530 var constraints = make([]*ast.Constraint, 0) 531 defCaus := causet.ToDeferredCauset(&perceptron.DeferredCausetInfo{ 532 Offset: offset, 533 Name: defCausDef.Name.Name, 534 FieldType: *defCausDef.Tp, 535 // TODO: remove this version field after there is no old version. 536 Version: perceptron.CurrLatestDeferredCausetInfoVersion, 537 }) 538 539 if !isExplicitTimeStamp() { 540 // Check and set TimestampFlag, OnUFIDelateNowFlag and NotNullFlag. 541 if defCaus.Tp == allegrosql.TypeTimestamp { 542 defCaus.Flag |= allegrosql.TimestampFlag 543 defCaus.Flag |= allegrosql.OnUFIDelateNowFlag 544 defCaus.Flag |= allegrosql.NotNullFlag 545 } 546 } 547 var err error 548 setOnUFIDelateNow := false 549 hasDefaultValue := false 550 hasNullFlag := false 551 if defCausDef.Options != nil { 552 length := types.UnspecifiedLength 553 554 keys := []*ast.IndexPartSpecification{ 555 { 556 DeferredCauset: defCausDef.Name, 557 Length: length, 558 }, 559 } 560 561 var sb strings.Builder 562 restoreFlags := format.RestoreStringSingleQuotes | format.RestoreKeyWordLowercase | format.RestoreNameBackQuotes | 563 format.RestoreSpacesAroundBinaryOperation 564 restoreCtx := format.NewRestoreCtx(restoreFlags, &sb) 565 566 for _, v := range defCausDef.Options { 567 switch v.Tp { 568 case ast.DeferredCausetOptionNotNull: 569 defCaus.Flag |= allegrosql.NotNullFlag 570 case ast.DeferredCausetOptionNull: 571 defCaus.Flag &= ^allegrosql.NotNullFlag 572 removeOnUFIDelateNowFlag(defCaus) 573 hasNullFlag = true 574 case ast.DeferredCausetOptionAutoIncrement: 575 defCaus.Flag |= allegrosql.AutoIncrementFlag 576 case ast.DeferredCausetOptionPrimaryKey: 577 // Check PriKeyFlag first to avoid extra duplicate constraints. 578 if defCaus.Flag&allegrosql.PriKeyFlag == 0 { 579 constraint := &ast.Constraint{Tp: ast.ConstraintPrimaryKey, Keys: keys} 580 constraints = append(constraints, constraint) 581 defCaus.Flag |= allegrosql.PriKeyFlag 582 } 583 case ast.DeferredCausetOptionUniqKey: 584 // Check UniqueFlag first to avoid extra duplicate constraints. 585 if defCaus.Flag&allegrosql.UniqueFlag == 0 { 586 constraint := &ast.Constraint{Tp: ast.ConstraintUniqKey, Keys: keys} 587 constraints = append(constraints, constraint) 588 defCaus.Flag |= allegrosql.UniqueKeyFlag 589 } 590 case ast.DeferredCausetOptionDefaultValue: 591 hasDefaultValue, err = setDefaultValue(ctx, defCaus, v) 592 if err != nil { 593 return nil, nil, errors.Trace(err) 594 } 595 removeOnUFIDelateNowFlag(defCaus) 596 case ast.DeferredCausetOptionOnUFIDelate: 597 // TODO: Support other time functions. 598 if defCaus.Tp == allegrosql.TypeTimestamp || defCaus.Tp == allegrosql.TypeDatetime { 599 if !memex.IsValidCurrentTimestampExpr(v.Expr, defCausDef.Tp) { 600 return nil, nil, ErrInvalidOnUFIDelate.GenWithStackByArgs(defCaus.Name) 601 } 602 } else { 603 return nil, nil, ErrInvalidOnUFIDelate.GenWithStackByArgs(defCaus.Name) 604 } 605 defCaus.Flag |= allegrosql.OnUFIDelateNowFlag 606 setOnUFIDelateNow = true 607 case ast.DeferredCausetOptionComment: 608 err := setDeferredCausetComment(ctx, defCaus, v) 609 if err != nil { 610 return nil, nil, errors.Trace(err) 611 } 612 case ast.DeferredCausetOptionGenerated: 613 sb.Reset() 614 err = v.Expr.Restore(restoreCtx) 615 if err != nil { 616 return nil, nil, errors.Trace(err) 617 } 618 defCaus.GeneratedExprString = sb.String() 619 defCaus.GeneratedStored = v.Stored 620 _, dependDefCausNames := findDependedDeferredCausetNames(defCausDef) 621 defCaus.Dependences = dependDefCausNames 622 case ast.DeferredCausetOptionDefCauslate: 623 if field_types.HasCharset(defCausDef.Tp) { 624 defCaus.FieldType.DefCauslate = v.StrValue 625 } 626 case ast.DeferredCausetOptionFulltext: 627 ctx.GetStochastikVars().StmtCtx.AppendWarning(ErrTableCantHandleFt) 628 case ast.DeferredCausetOptionCheck: 629 ctx.GetStochastikVars().StmtCtx.AppendWarning(ErrUnsupportedConstraintCheck.GenWithStackByArgs("DeferredCauset check")) 630 } 631 } 632 } 633 634 processDefaultValue(defCaus, hasDefaultValue, setOnUFIDelateNow) 635 636 processDeferredCausetFlags(defCaus) 637 638 err = checkPriKeyConstraint(defCaus, hasDefaultValue, hasNullFlag, outPriKeyConstraint) 639 if err != nil { 640 return nil, nil, errors.Trace(err) 641 } 642 err = checkDeferredCausetValueConstraint(defCaus, defCaus.DefCauslate) 643 if err != nil { 644 return nil, nil, errors.Trace(err) 645 } 646 err = checkDefaultValue(ctx, defCaus, hasDefaultValue) 647 if err != nil { 648 return nil, nil, errors.Trace(err) 649 } 650 err = checkDeferredCausetFieldLength(defCaus) 651 if err != nil { 652 return nil, nil, errors.Trace(err) 653 } 654 return defCaus, constraints, nil 655 } 656 657 // getDefaultValue will get the default value for defCausumn. 658 // 1: get the expr restored string for the defCausumn which uses sequence next value as default value. 659 // 2: get specific default value for the other defCausumn. 660 func getDefaultValue(ctx stochastikctx.Context, defCaus *causet.DeferredCauset, c *ast.DeferredCausetOption) (interface{}, bool, error) { 661 tp, fsp := defCaus.FieldType.Tp, defCaus.FieldType.Decimal 662 if tp == allegrosql.TypeTimestamp || tp == allegrosql.TypeDatetime { 663 switch x := c.Expr.(type) { 664 case *ast.FuncCallExpr: 665 if x.FnName.L == ast.CurrentTimestamp { 666 defaultFsp := 0 667 if len(x.Args) == 1 { 668 if val := x.Args[0].(*driver.ValueExpr); val != nil { 669 defaultFsp = int(val.GetInt64()) 670 } 671 } 672 if defaultFsp != fsp { 673 return nil, false, ErrInvalidDefaultValue.GenWithStackByArgs(defCaus.Name.O) 674 } 675 } 676 } 677 vd, err := memex.GetTimeValue(ctx, c.Expr, tp, int8(fsp)) 678 value := vd.GetValue() 679 if err != nil { 680 return nil, false, ErrInvalidDefaultValue.GenWithStackByArgs(defCaus.Name.O) 681 } 682 683 // Value is nil means `default null`. 684 if value == nil { 685 return nil, false, nil 686 } 687 688 // If value is types.Time, convert it to string. 689 if vv, ok := value.(types.Time); ok { 690 return vv.String(), false, nil 691 } 692 693 return value, false, nil 694 } 695 // handle default next value of sequence. (keep the expr string) 696 str, isSeqExpr, err := tryToGetSequenceDefaultValue(c) 697 if err != nil { 698 return nil, false, errors.Trace(err) 699 } 700 if isSeqExpr { 701 return str, true, nil 702 } 703 704 // evaluate the non-sequence expr to a certain value. 705 v, err := memex.EvalAstExpr(ctx, c.Expr) 706 if err != nil { 707 return nil, false, errors.Trace(err) 708 } 709 710 if v.IsNull() { 711 return nil, false, nil 712 } 713 714 if v.HoTT() == types.HoTTBinaryLiteral || v.HoTT() == types.HoTTMysqlBit { 715 if tp == allegrosql.TypeBit || 716 tp == allegrosql.TypeString || tp == allegrosql.TypeVarchar || tp == allegrosql.TypeVarString || 717 tp == allegrosql.TypeBlob || tp == allegrosql.TypeLongBlob || tp == allegrosql.TypeMediumBlob || tp == allegrosql.TypeTinyBlob || 718 tp == allegrosql.TypeJSON { 719 // For BinaryLiteral / string fields, when getting default value we cast the value into BinaryLiteral{}, thus we return 720 // its raw string content here. 721 return v.GetBinaryLiteral().ToString(), false, nil 722 } 723 // For other HoTT of fields (e.g. INT), we supply its integer as string value. 724 value, err := v.GetBinaryLiteral().ToInt(ctx.GetStochastikVars().StmtCtx) 725 if err != nil { 726 return nil, false, err 727 } 728 return strconv.FormatUint(value, 10), false, nil 729 } 730 731 switch tp { 732 case allegrosql.TypeSet: 733 val, err := setSetDefaultValue(v, defCaus) 734 return val, false, err 735 case allegrosql.TypeDuration: 736 if v, err = v.ConvertTo(ctx.GetStochastikVars().StmtCtx, &defCaus.FieldType); err != nil { 737 return "", false, errors.Trace(err) 738 } 739 case allegrosql.TypeBit: 740 if v.HoTT() == types.HoTTInt64 || v.HoTT() == types.HoTTUint64 { 741 // For BIT fields, convert int into BinaryLiteral. 742 return types.NewBinaryLiteralFromUint(v.GetUint64(), -1).ToString(), false, nil 743 } 744 } 745 746 val, err := v.ToString() 747 return val, false, err 748 } 749 750 func tryToGetSequenceDefaultValue(c *ast.DeferredCausetOption) (expr string, isExpr bool, err error) { 751 if f, ok := c.Expr.(*ast.FuncCallExpr); ok && f.FnName.L == ast.NextVal { 752 var sb strings.Builder 753 restoreFlags := format.RestoreStringSingleQuotes | format.RestoreKeyWordLowercase | format.RestoreNameBackQuotes | 754 format.RestoreSpacesAroundBinaryOperation 755 restoreCtx := format.NewRestoreCtx(restoreFlags, &sb) 756 if err := c.Expr.Restore(restoreCtx); err != nil { 757 return "", true, err 758 } 759 return sb.String(), true, nil 760 } 761 return "", false, nil 762 } 763 764 // setSetDefaultValue sets the default value for the set type. See https://dev.allegrosql.com/doc/refman/5.7/en/set.html. 765 func setSetDefaultValue(v types.Causet, defCaus *causet.DeferredCauset) (string, error) { 766 if v.HoTT() == types.HoTTInt64 { 767 setCnt := len(defCaus.Elems) 768 maxLimit := int64(1<<uint(setCnt) - 1) 769 val := v.GetInt64() 770 if val < 1 || val > maxLimit { 771 return "", ErrInvalidDefaultValue.GenWithStackByArgs(defCaus.Name.O) 772 } 773 setVal, err := types.ParseSetValue(defCaus.Elems, uint64(val)) 774 if err != nil { 775 return "", errors.Trace(err) 776 } 777 v.SetMysqlSet(setVal, defCaus.DefCauslate) 778 return v.ToString() 779 } 780 781 str, err := v.ToString() 782 if err != nil { 783 return "", errors.Trace(err) 784 } 785 if str == "" { 786 return str, nil 787 } 788 789 ctor := defCauslate.GetDefCauslator(defCaus.DefCauslate) 790 valMap := make(map[string]struct{}, len(defCaus.Elems)) 791 dVals := strings.Split(str, ",") 792 for _, dv := range dVals { 793 valMap[string(ctor.Key(dv))] = struct{}{} 794 } 795 var existCnt int 796 for dv := range valMap { 797 for i := range defCaus.Elems { 798 e := string(ctor.Key(defCaus.Elems[i])) 799 if e == dv { 800 existCnt++ 801 break 802 } 803 } 804 } 805 if existCnt != len(valMap) { 806 return "", ErrInvalidDefaultValue.GenWithStackByArgs(defCaus.Name.O) 807 } 808 setVal, err := types.ParseSetName(defCaus.Elems, str, defCaus.DefCauslate) 809 if err != nil { 810 return "", ErrInvalidDefaultValue.GenWithStackByArgs(defCaus.Name.O) 811 } 812 v.SetMysqlSet(setVal, defCaus.DefCauslate) 813 814 return v.ToString() 815 } 816 817 func removeOnUFIDelateNowFlag(c *causet.DeferredCauset) { 818 // For timestamp DefCaus, if it is set null or default value, 819 // OnUFIDelateNowFlag should be removed. 820 if allegrosql.HasTimestampFlag(c.Flag) { 821 c.Flag &= ^allegrosql.OnUFIDelateNowFlag 822 } 823 } 824 825 func processDefaultValue(c *causet.DeferredCauset, hasDefaultValue bool, setOnUFIDelateNow bool) { 826 setTimestamFIDelefaultValue(c, hasDefaultValue, setOnUFIDelateNow) 827 828 setYearDefaultValue(c, hasDefaultValue) 829 830 // Set `NoDefaultValueFlag` if this field doesn't have a default value and 831 // it is `not null` and not an `AUTO_INCREMENT` field or `TIMESTAMP` field. 832 setNoDefaultValueFlag(c, hasDefaultValue) 833 } 834 835 func setYearDefaultValue(c *causet.DeferredCauset, hasDefaultValue bool) { 836 if hasDefaultValue { 837 return 838 } 839 840 if c.Tp == allegrosql.TypeYear && allegrosql.HasNotNullFlag(c.Flag) { 841 if err := c.SetDefaultValue("0000"); err != nil { 842 logutil.BgLogger().Error("set default value failed", zap.Error(err)) 843 } 844 } 845 } 846 847 func setTimestamFIDelefaultValue(c *causet.DeferredCauset, hasDefaultValue bool, setOnUFIDelateNow bool) { 848 if hasDefaultValue { 849 return 850 } 851 852 // For timestamp DefCaus, if is not set default value or not set null, use current timestamp. 853 if allegrosql.HasTimestampFlag(c.Flag) && allegrosql.HasNotNullFlag(c.Flag) { 854 if setOnUFIDelateNow { 855 if err := c.SetDefaultValue(types.ZeroDatetimeStr); err != nil { 856 logutil.BgLogger().Error("set default value failed", zap.Error(err)) 857 } 858 } else { 859 if err := c.SetDefaultValue(strings.ToUpper(ast.CurrentTimestamp)); err != nil { 860 logutil.BgLogger().Error("set default value failed", zap.Error(err)) 861 } 862 } 863 } 864 } 865 866 func setNoDefaultValueFlag(c *causet.DeferredCauset, hasDefaultValue bool) { 867 if hasDefaultValue { 868 return 869 } 870 871 if !allegrosql.HasNotNullFlag(c.Flag) { 872 return 873 } 874 875 // Check if it is an `AUTO_INCREMENT` field or `TIMESTAMP` field. 876 if !allegrosql.HasAutoIncrementFlag(c.Flag) && !allegrosql.HasTimestampFlag(c.Flag) { 877 c.Flag |= allegrosql.NoDefaultValueFlag 878 } 879 } 880 881 func checkDefaultValue(ctx stochastikctx.Context, c *causet.DeferredCauset, hasDefaultValue bool) error { 882 if !hasDefaultValue { 883 return nil 884 } 885 886 if c.GetDefaultValue() != nil { 887 if c.DefaultIsExpr { 888 return nil 889 } 890 if _, err := causet.GetDefCausDefaultValue(ctx, c.ToInfo()); err != nil { 891 return types.ErrInvalidDefault.GenWithStackByArgs(c.Name) 892 } 893 return nil 894 } 895 // Primary key default null is invalid. 896 if allegrosql.HasPriKeyFlag(c.Flag) { 897 return ErrPrimaryCantHaveNull 898 } 899 900 // Set not null but default null is invalid. 901 if allegrosql.HasNotNullFlag(c.Flag) { 902 return types.ErrInvalidDefault.GenWithStackByArgs(c.Name) 903 } 904 905 return nil 906 } 907 908 // checkPriKeyConstraint check all parts of a PRIMARY KEY must be NOT NULL 909 func checkPriKeyConstraint(defCaus *causet.DeferredCauset, hasDefaultValue, hasNullFlag bool, outPriKeyConstraint *ast.Constraint) error { 910 // Primary key should not be null. 911 if allegrosql.HasPriKeyFlag(defCaus.Flag) && hasDefaultValue && defCaus.GetDefaultValue() == nil { 912 return types.ErrInvalidDefault.GenWithStackByArgs(defCaus.Name) 913 } 914 // Set primary key flag for outer primary key constraint. 915 // Such as: create causet t1 (id int , age int, primary key(id)) 916 if !allegrosql.HasPriKeyFlag(defCaus.Flag) && outPriKeyConstraint != nil { 917 for _, key := range outPriKeyConstraint.Keys { 918 if key.Expr == nil && key.DeferredCauset.Name.L != defCaus.Name.L { 919 continue 920 } 921 defCaus.Flag |= allegrosql.PriKeyFlag 922 break 923 } 924 } 925 // Primary key should not be null. 926 if allegrosql.HasPriKeyFlag(defCaus.Flag) && hasNullFlag { 927 return ErrPrimaryCantHaveNull 928 } 929 return nil 930 } 931 932 func checkDeferredCausetValueConstraint(defCaus *causet.DeferredCauset, defCauslation string) error { 933 if defCaus.Tp != allegrosql.TypeEnum && defCaus.Tp != allegrosql.TypeSet { 934 return nil 935 } 936 valueMap := make(map[string]bool, len(defCaus.Elems)) 937 ctor := defCauslate.GetDefCauslator(defCauslation) 938 for i := range defCaus.Elems { 939 val := string(ctor.Key(defCaus.Elems[i])) 940 if _, ok := valueMap[val]; ok { 941 tpStr := "ENUM" 942 if defCaus.Tp == allegrosql.TypeSet { 943 tpStr = "SET" 944 } 945 return types.ErrDuplicatedValueInType.GenWithStackByArgs(defCaus.Name, defCaus.Elems[i], tpStr) 946 } 947 valueMap[val] = true 948 } 949 return nil 950 } 951 952 func checkDuplicateDeferredCauset(defcaus []*perceptron.DeferredCausetInfo) error { 953 defCausNames := set.StringSet{} 954 for _, defCaus := range defcaus { 955 defCausName := defCaus.Name 956 if defCausNames.Exist(defCausName.L) { 957 return schemareplicant.ErrDeferredCausetExists.GenWithStackByArgs(defCausName.O) 958 } 959 defCausNames.Insert(defCausName.L) 960 } 961 return nil 962 } 963 964 func containsDeferredCausetOption(defCausDef *ast.DeferredCausetDef, opTp ast.DeferredCausetOptionType) bool { 965 for _, option := range defCausDef.Options { 966 if option.Tp == opTp { 967 return true 968 } 969 } 970 return false 971 } 972 973 // IsAutoRandomDeferredCausetID returns true if the given defCausumn ID belongs to an auto_random defCausumn. 974 func IsAutoRandomDeferredCausetID(tblInfo *perceptron.TableInfo, defCausID int64) bool { 975 return tblInfo.PKIsHandle && tblInfo.ContainsAutoRandomBits() && tblInfo.GetPkDefCausInfo().ID == defCausID 976 } 977 978 func checkGeneratedDeferredCauset(defCausDefs []*ast.DeferredCausetDef) error { 979 var defCausName2Generation = make(map[string]defCausumnGenerationInDBS, len(defCausDefs)) 980 var exists bool 981 var autoIncrementDeferredCauset string 982 for i, defCausDef := range defCausDefs { 983 for _, option := range defCausDef.Options { 984 if option.Tp == ast.DeferredCausetOptionGenerated { 985 if err := checkIllegalFn4GeneratedDeferredCauset(defCausDef.Name.Name.L, option.Expr); err != nil { 986 return errors.Trace(err) 987 } 988 } 989 } 990 if containsDeferredCausetOption(defCausDef, ast.DeferredCausetOptionAutoIncrement) { 991 exists, autoIncrementDeferredCauset = true, defCausDef.Name.Name.L 992 } 993 generated, depDefCauss := findDependedDeferredCausetNames(defCausDef) 994 if !generated { 995 defCausName2Generation[defCausDef.Name.Name.L] = defCausumnGenerationInDBS{ 996 position: i, 997 generated: false, 998 } 999 } else { 1000 defCausName2Generation[defCausDef.Name.Name.L] = defCausumnGenerationInDBS{ 1001 position: i, 1002 generated: true, 1003 dependences: depDefCauss, 1004 } 1005 } 1006 } 1007 1008 // Check whether the generated defCausumn refers to any auto-increment defCausumns 1009 if exists { 1010 for defCausName, generated := range defCausName2Generation { 1011 if _, found := generated.dependences[autoIncrementDeferredCauset]; found { 1012 return ErrGeneratedDeferredCausetRefAutoInc.GenWithStackByArgs(defCausName) 1013 } 1014 } 1015 } 1016 1017 for _, defCausDef := range defCausDefs { 1018 defCausName := defCausDef.Name.Name.L 1019 if err := verifyDeferredCausetGeneration(defCausName2Generation, defCausName); err != nil { 1020 return errors.Trace(err) 1021 } 1022 } 1023 return nil 1024 } 1025 1026 func checkTooLongDeferredCauset(defcaus []*perceptron.DeferredCausetInfo) error { 1027 for _, defCaus := range defcaus { 1028 defCausName := defCaus.Name.O 1029 if len(defCausName) > allegrosql.MaxDeferredCausetNameLength { 1030 return ErrTooLongIdent.GenWithStackByArgs(defCausName) 1031 } 1032 } 1033 return nil 1034 } 1035 1036 func checkTooManyDeferredCausets(defCausDefs []*perceptron.DeferredCausetInfo) error { 1037 if uint32(len(defCausDefs)) > atomic.LoadUint32(&TableDeferredCausetCountLimit) { 1038 return errTooManyFields 1039 } 1040 return nil 1041 } 1042 1043 // checkDeferredCausetsAttributes checks attributes for multiple defCausumns. 1044 func checkDeferredCausetsAttributes(defCausDefs []*perceptron.DeferredCausetInfo) error { 1045 for _, defCausDef := range defCausDefs { 1046 if err := checkDeferredCausetAttributes(defCausDef.Name.O, &defCausDef.FieldType); err != nil { 1047 return errors.Trace(err) 1048 } 1049 } 1050 return nil 1051 } 1052 1053 func checkDeferredCausetFieldLength(defCaus *causet.DeferredCauset) error { 1054 if defCaus.Tp == allegrosql.TypeVarchar { 1055 if err := IsTooBigFieldLength(defCaus.Flen, defCaus.Name.O, defCaus.Charset); err != nil { 1056 return errors.Trace(err) 1057 } 1058 } 1059 1060 return nil 1061 } 1062 1063 // IsTooBigFieldLength check if the varchar type defCausumn exceeds the maximum length limit. 1064 func IsTooBigFieldLength(defCausDefTpFlen int, defCausDefName, setCharset string) error { 1065 desc, err := charset.GetCharsetDesc(setCharset) 1066 if err != nil { 1067 return errors.Trace(err) 1068 } 1069 maxFlen := allegrosql.MaxFieldVarCharLength 1070 maxFlen /= desc.Maxlen 1071 if defCausDefTpFlen != types.UnspecifiedLength && defCausDefTpFlen > maxFlen { 1072 return types.ErrTooBigFieldLength.GenWithStack("DeferredCauset length too big for defCausumn '%s' (max = %d); use BLOB or TEXT instead", defCausDefName, maxFlen) 1073 } 1074 return nil 1075 } 1076 1077 // checkDeferredCausetAttributes check attributes for single defCausumn. 1078 func checkDeferredCausetAttributes(defCausName string, tp *types.FieldType) error { 1079 switch tp.Tp { 1080 case allegrosql.TypeNewDecimal, allegrosql.TypeDouble, allegrosql.TypeFloat: 1081 if tp.Flen < tp.Decimal { 1082 return types.ErrMBiggerThanD.GenWithStackByArgs(defCausName) 1083 } 1084 case allegrosql.TypeDatetime, allegrosql.TypeDuration, allegrosql.TypeTimestamp: 1085 if tp.Decimal != int(types.UnspecifiedFsp) && (tp.Decimal < int(types.MinFsp) || tp.Decimal > int(types.MaxFsp)) { 1086 return types.ErrTooBigPrecision.GenWithStackByArgs(tp.Decimal, defCausName, types.MaxFsp) 1087 } 1088 } 1089 return nil 1090 } 1091 1092 func checkDuplicateConstraint(namesMap map[string]bool, name string, foreign bool) error { 1093 if name == "" { 1094 return nil 1095 } 1096 nameLower := strings.ToLower(name) 1097 if namesMap[nameLower] { 1098 if foreign { 1099 return schemareplicant.ErrCannotAddForeign 1100 } 1101 return ErrDupKeyName.GenWithStack("duplicate key name %s", name) 1102 } 1103 namesMap[nameLower] = true 1104 return nil 1105 } 1106 1107 func setEmptyConstraintName(namesMap map[string]bool, constr *ast.Constraint, foreign bool) { 1108 if constr.Name == "" && len(constr.Keys) > 0 { 1109 var defCausName string 1110 for _, keyPart := range constr.Keys { 1111 if keyPart.Expr != nil { 1112 defCausName = "memex_index" 1113 } 1114 } 1115 if defCausName == "" { 1116 defCausName = constr.Keys[0].DeferredCauset.Name.L 1117 } 1118 constrName := defCausName 1119 i := 2 1120 if strings.EqualFold(constrName, allegrosql.PrimaryKeyName) { 1121 constrName = fmt.Sprintf("%s_%d", constrName, 2) 1122 i = 3 1123 } 1124 for namesMap[constrName] { 1125 // We loop forever until we find constrName that haven't been used. 1126 if foreign { 1127 constrName = fmt.Sprintf("fk_%s_%d", defCausName, i) 1128 } else { 1129 constrName = fmt.Sprintf("%s_%d", defCausName, i) 1130 } 1131 i++ 1132 } 1133 constr.Name = constrName 1134 namesMap[constrName] = true 1135 } 1136 } 1137 1138 func checkConstraintNames(constraints []*ast.Constraint) error { 1139 constrNames := map[string]bool{} 1140 fkNames := map[string]bool{} 1141 1142 // Check not empty constraint name whether is duplicated. 1143 for _, constr := range constraints { 1144 if constr.Tp == ast.ConstraintForeignKey { 1145 err := checkDuplicateConstraint(fkNames, constr.Name, true) 1146 if err != nil { 1147 return errors.Trace(err) 1148 } 1149 } else { 1150 err := checkDuplicateConstraint(constrNames, constr.Name, false) 1151 if err != nil { 1152 return errors.Trace(err) 1153 } 1154 } 1155 } 1156 1157 // Set empty constraint names. 1158 for _, constr := range constraints { 1159 if constr.Tp == ast.ConstraintForeignKey { 1160 setEmptyConstraintName(fkNames, constr, true) 1161 } else { 1162 setEmptyConstraintName(constrNames, constr, false) 1163 } 1164 } 1165 1166 return nil 1167 } 1168 1169 // checkInvisibleIndexOnPK check if primary key is invisible index. 1170 // Note: PKIsHandle == true means the causet already has a visible primary key, 1171 // we do not need do a check for this case and return directly, 1172 // because whether primary key is invisible has been check when creating causet. 1173 func checkInvisibleIndexOnPK(tblInfo *perceptron.TableInfo) error { 1174 if tblInfo.PKIsHandle { 1175 return nil 1176 } 1177 pk := getPrimaryKey(tblInfo) 1178 if pk != nil && pk.Invisible { 1179 return ErrPHoTTexCantBeInvisible 1180 } 1181 return nil 1182 } 1183 1184 // getPrimaryKey extract the primary key in a causet and return `IndexInfo` 1185 // The returned primary key could be explicit or implicit. 1186 // If there is no explicit primary key in causet, 1187 // the first UNIQUE INDEX on NOT NULL defCausumns will be the implicit primary key. 1188 // For more information about implicit primary key, see 1189 // https://dev.allegrosql.com/doc/refman/8.0/en/invisible-indexes.html 1190 func getPrimaryKey(tblInfo *perceptron.TableInfo) *perceptron.IndexInfo { 1191 var implicitPK *perceptron.IndexInfo 1192 1193 for _, key := range tblInfo.Indices { 1194 if key.Primary { 1195 // causet has explicit primary key 1196 return key 1197 } 1198 // The case index without any defCausumns should never happen, but still do a check here 1199 if len(key.DeferredCausets) == 0 { 1200 continue 1201 } 1202 // find the first unique key with NOT NULL defCausumns 1203 if implicitPK == nil && key.Unique { 1204 // ensure all defCausumns in unique key have NOT NULL flag 1205 allDefCausNotNull := true 1206 skip := false 1207 for _, idxDefCaus := range key.DeferredCausets { 1208 defCaus := perceptron.FindDeferredCausetInfo(tblInfo.DefCauss(), idxDefCaus.Name.L) 1209 // This index has a defCausumn in DeleteOnly state, 1210 // or it is memex index (it defined on a hidden defCausumn), 1211 // it can not be implicit PK, go to next index iterator 1212 if defCaus == nil || defCaus.Hidden { 1213 skip = true 1214 break 1215 } 1216 if !allegrosql.HasNotNullFlag(defCaus.Flag) { 1217 allDefCausNotNull = false 1218 break 1219 } 1220 } 1221 if skip { 1222 continue 1223 } 1224 if allDefCausNotNull { 1225 implicitPK = key 1226 } 1227 } 1228 } 1229 return implicitPK 1230 } 1231 1232 func setTableAutoRandomBits(ctx stochastikctx.Context, tbInfo *perceptron.TableInfo, defCausDefs []*ast.DeferredCausetDef) error { 1233 pkDefCausName := tbInfo.GetPkName() 1234 for _, defCaus := range defCausDefs { 1235 if containsDeferredCausetOption(defCaus, ast.DeferredCausetOptionAutoRandom) { 1236 if defCaus.Tp.Tp != allegrosql.TypeLonglong { 1237 return ErrInvalidAutoRandom.GenWithStackByArgs( 1238 fmt.Sprintf(autoid.AutoRandomOnNonBigIntDeferredCauset, types.TypeStr(defCaus.Tp.Tp))) 1239 } 1240 if !tbInfo.PKIsHandle || defCaus.Name.Name.L != pkDefCausName.L { 1241 errMsg := fmt.Sprintf(autoid.AutoRandomPKisNotHandleErrMsg, defCaus.Name.Name.O) 1242 return ErrInvalidAutoRandom.GenWithStackByArgs(errMsg) 1243 } 1244 if containsDeferredCausetOption(defCaus, ast.DeferredCausetOptionAutoIncrement) { 1245 return ErrInvalidAutoRandom.GenWithStackByArgs(autoid.AutoRandomIncompatibleWithAutoIncErrMsg) 1246 } 1247 if containsDeferredCausetOption(defCaus, ast.DeferredCausetOptionDefaultValue) { 1248 return ErrInvalidAutoRandom.GenWithStackByArgs(autoid.AutoRandomIncompatibleWithDefaultValueErrMsg) 1249 } 1250 1251 autoRandBits, err := extractAutoRandomBitsFromDefCausDef(defCaus) 1252 if err != nil { 1253 return errors.Trace(err) 1254 } 1255 1256 layout := autoid.NewAutoRandomIDLayout(defCaus.Tp, autoRandBits) 1257 if autoRandBits == 0 { 1258 return ErrInvalidAutoRandom.GenWithStackByArgs(autoid.AutoRandomNonPositive) 1259 } else if autoRandBits > autoid.MaxAutoRandomBits { 1260 errMsg := fmt.Sprintf(autoid.AutoRandomOverflowErrMsg, 1261 autoid.MaxAutoRandomBits, autoRandBits, defCaus.Name.Name.O) 1262 return ErrInvalidAutoRandom.GenWithStackByArgs(errMsg) 1263 } 1264 tbInfo.AutoRandomBits = autoRandBits 1265 1266 msg := fmt.Sprintf(autoid.AutoRandomAvailableAllocTimesNote, layout.IncrementalBitsCapacity()) 1267 ctx.GetStochastikVars().StmtCtx.AppendNote(errors.Errorf(msg)) 1268 } 1269 } 1270 return nil 1271 } 1272 1273 func extractAutoRandomBitsFromDefCausDef(defCausDef *ast.DeferredCausetDef) (uint64, error) { 1274 for _, op := range defCausDef.Options { 1275 if op.Tp == ast.DeferredCausetOptionAutoRandom { 1276 return convertAutoRandomBitsToUnsigned(op.AutoRandomBitLength) 1277 } 1278 } 1279 return 0, nil 1280 } 1281 1282 func convertAutoRandomBitsToUnsigned(autoRandomBits int) (uint64, error) { 1283 if autoRandomBits == types.UnspecifiedLength { 1284 return autoid.DefaultAutoRandomBits, nil 1285 } else if autoRandomBits < 0 { 1286 return 0, ErrInvalidAutoRandom.GenWithStackByArgs(autoid.AutoRandomNonPositive) 1287 } 1288 return uint64(autoRandomBits), nil 1289 } 1290 1291 func buildTableInfo( 1292 ctx stochastikctx.Context, 1293 blockName perceptron.CIStr, 1294 defcaus []*causet.DeferredCauset, 1295 constraints []*ast.Constraint, 1296 charset string, 1297 defCauslate string) (tbInfo *perceptron.TableInfo, err error) { 1298 tbInfo = &perceptron.TableInfo{ 1299 Name: blockName, 1300 Version: perceptron.CurrLatestTableInfoVersion, 1301 Charset: charset, 1302 DefCauslate: defCauslate, 1303 } 1304 tblDeferredCausets := make([]*causet.DeferredCauset, 0, len(defcaus)) 1305 for _, v := range defcaus { 1306 v.ID = allocateDeferredCausetID(tbInfo) 1307 tbInfo.DeferredCausets = append(tbInfo.DeferredCausets, v.ToInfo()) 1308 tblDeferredCausets = append(tblDeferredCausets, causet.ToDeferredCauset(v.ToInfo())) 1309 } 1310 for _, constr := range constraints { 1311 // Build hidden defCausumns if necessary. 1312 hiddenDefCauss, err := buildHiddenDeferredCausetInfo(ctx, constr.Keys, perceptron.NewCIStr(constr.Name), tbInfo, tblDeferredCausets) 1313 if err != nil { 1314 return nil, err 1315 } 1316 for _, hiddenDefCaus := range hiddenDefCauss { 1317 hiddenDefCaus.State = perceptron.StatePublic 1318 hiddenDefCaus.ID = allocateDeferredCausetID(tbInfo) 1319 hiddenDefCaus.Offset = len(tbInfo.DeferredCausets) 1320 tbInfo.DeferredCausets = append(tbInfo.DeferredCausets, hiddenDefCaus) 1321 tblDeferredCausets = append(tblDeferredCausets, causet.ToDeferredCauset(hiddenDefCaus)) 1322 } 1323 if constr.Tp == ast.ConstraintForeignKey { 1324 for _, fk := range tbInfo.ForeignKeys { 1325 if fk.Name.L == strings.ToLower(constr.Name) { 1326 return nil, schemareplicant.ErrCannotAddForeign 1327 } 1328 } 1329 fk, err := buildFKInfo(perceptron.NewCIStr(constr.Name), constr.Keys, constr.Refer, defcaus, tbInfo) 1330 if err != nil { 1331 return nil, err 1332 } 1333 fk.State = perceptron.StatePublic 1334 1335 tbInfo.ForeignKeys = append(tbInfo.ForeignKeys, fk) 1336 continue 1337 } 1338 if constr.Tp == ast.ConstraintPrimaryKey { 1339 lastDefCaus, err := checkPKOnGeneratedDeferredCauset(tbInfo, constr.Keys) 1340 if err != nil { 1341 return nil, err 1342 } 1343 if !config.GetGlobalConfig().AlterPrimaryKey { 1344 singleIntPK := isSingleIntPK(constr, lastDefCaus) 1345 clusteredIdx := ctx.GetStochastikVars().EnableClusteredIndex 1346 if singleIntPK || clusteredIdx { 1347 // Primary key cannot be invisible. 1348 if constr.Option != nil && constr.Option.Visibility == ast.IndexVisibilityInvisible { 1349 return nil, ErrPHoTTexCantBeInvisible 1350 } 1351 } 1352 if singleIntPK { 1353 tbInfo.PKIsHandle = true 1354 // Avoid creating index for PK handle defCausumn. 1355 continue 1356 } 1357 if clusteredIdx { 1358 tbInfo.IsCommonHandle = true 1359 } 1360 } 1361 } 1362 1363 if constr.Tp == ast.ConstraintFulltext { 1364 sc := ctx.GetStochastikVars().StmtCtx 1365 sc.AppendWarning(ErrTableCantHandleFt) 1366 continue 1367 } 1368 // build index info. 1369 idxInfo, err := buildIndexInfo(tbInfo, perceptron.NewCIStr(constr.Name), constr.Keys, perceptron.StatePublic) 1370 if err != nil { 1371 return nil, errors.Trace(err) 1372 } 1373 if len(hiddenDefCauss) > 0 { 1374 addIndexDeferredCausetFlag(tbInfo, idxInfo) 1375 } 1376 // check if the index is primary or unique. 1377 switch constr.Tp { 1378 case ast.ConstraintPrimaryKey: 1379 idxInfo.Primary = true 1380 idxInfo.Unique = true 1381 idxInfo.Name = perceptron.NewCIStr(allegrosql.PrimaryKeyName) 1382 case ast.ConstraintUniq, ast.ConstraintUniqKey, ast.ConstraintUniqIndex: 1383 idxInfo.Unique = true 1384 } 1385 // set index type. 1386 if constr.Option != nil { 1387 idxInfo.Comment, err = validateCommentLength(ctx.GetStochastikVars(), idxInfo.Name.String(), constr.Option) 1388 if err != nil { 1389 return nil, errors.Trace(err) 1390 } 1391 if constr.Option.Visibility == ast.IndexVisibilityInvisible { 1392 idxInfo.Invisible = true 1393 } 1394 if constr.Option.Tp == perceptron.IndexTypeInvalid { 1395 // Use btree as default index type. 1396 idxInfo.Tp = perceptron.IndexTypeBtree 1397 } else { 1398 idxInfo.Tp = constr.Option.Tp 1399 } 1400 } else { 1401 // Use btree as default index type. 1402 idxInfo.Tp = perceptron.IndexTypeBtree 1403 } 1404 idxInfo.ID = allocateIndexID(tbInfo) 1405 tbInfo.Indices = append(tbInfo.Indices, idxInfo) 1406 } 1407 return 1408 } 1409 1410 func isSingleIntPK(constr *ast.Constraint, lastDefCaus *perceptron.DeferredCausetInfo) bool { 1411 if len(constr.Keys) != 1 { 1412 return false 1413 } 1414 switch lastDefCaus.Tp { 1415 case allegrosql.TypeLong, allegrosql.TypeLonglong, 1416 allegrosql.TypeTiny, allegrosql.TypeShort, allegrosql.TypeInt24: 1417 return true 1418 } 1419 return false 1420 } 1421 1422 // checkTableInfoValidExtra is like checkTableInfoValid, but also assumes the 1423 // causet info comes from untrusted source and performs further checks such as 1424 // name length and defCausumn count. 1425 // (checkTableInfoValid is also used in repairing objects which don't perform 1426 // these checks. Perhaps the two functions should be merged together regardless?) 1427 func checkTableInfoValidExtra(tbInfo *perceptron.TableInfo) error { 1428 if err := checkTooLongTable(tbInfo.Name); err != nil { 1429 return err 1430 } 1431 1432 if err := checkDuplicateDeferredCauset(tbInfo.DeferredCausets); err != nil { 1433 return err 1434 } 1435 if err := checkTooLongDeferredCauset(tbInfo.DeferredCausets); err != nil { 1436 return err 1437 } 1438 if err := checkTooManyDeferredCausets(tbInfo.DeferredCausets); err != nil { 1439 return errors.Trace(err) 1440 } 1441 if err := checkDeferredCausetsAttributes(tbInfo.DeferredCausets); err != nil { 1442 return errors.Trace(err) 1443 } 1444 1445 // FIXME: perform checkConstraintNames 1446 if err := checkCharsetAndDefCauslation(tbInfo.Charset, tbInfo.DefCauslate); err != nil { 1447 return errors.Trace(err) 1448 } 1449 1450 oldState := tbInfo.State 1451 tbInfo.State = perceptron.StatePublic 1452 err := checkTableInfoValid(tbInfo) 1453 tbInfo.State = oldState 1454 return err 1455 } 1456 1457 func checkTableInfoValidWithStmt(ctx stochastikctx.Context, tbInfo *perceptron.TableInfo, s *ast.CreateTableStmt) error { 1458 // All of these rely on the AST structure of memexs, which were 1459 // lost in the perceptron (got serialized into strings). 1460 if err := checkGeneratedDeferredCauset(s.DefCauss); err != nil { 1461 return errors.Trace(err) 1462 } 1463 if s.Partition != nil { 1464 err := checkPartitionExprValid(ctx, tbInfo, s.Partition.Expr) 1465 if err != nil { 1466 return errors.Trace(err) 1467 } 1468 1469 pi := tbInfo.Partition 1470 if pi != nil { 1471 switch pi.Type { 1472 case perceptron.PartitionTypeRange: 1473 err = checkPartitionByRange(ctx, tbInfo, s) 1474 case perceptron.PartitionTypeHash: 1475 err = checkPartitionByHash(ctx, tbInfo, s) 1476 } 1477 if err != nil { 1478 return errors.Trace(err) 1479 } 1480 } 1481 1482 if err = checkPartitioningKeysConstraints(ctx, s, tbInfo); err != nil { 1483 return errors.Trace(err) 1484 } 1485 } 1486 return nil 1487 } 1488 1489 // checkTableInfoValid uses to check causet info valid. This is used to validate causet info. 1490 func checkTableInfoValid(tblInfo *perceptron.TableInfo) error { 1491 _, err := blocks.TableFromMeta(nil, tblInfo) 1492 if err != nil { 1493 return err 1494 } 1495 return checkInvisibleIndexOnPK(tblInfo) 1496 } 1497 1498 func buildTableInfoWithLike(ident ast.Ident, referTblInfo *perceptron.TableInfo) (*perceptron.TableInfo, error) { 1499 // Check the referred causet is a real causet object. 1500 if referTblInfo.IsSequence() || referTblInfo.IsView() { 1501 return nil, ErrWrongObject.GenWithStackByArgs(ident.Schema, referTblInfo.Name, "BASE TABLE") 1502 } 1503 tblInfo := *referTblInfo 1504 // Check non-public defCausumn and adjust defCausumn offset. 1505 newDeferredCausets := referTblInfo.DefCauss() 1506 newIndices := make([]*perceptron.IndexInfo, 0, len(tblInfo.Indices)) 1507 for _, idx := range tblInfo.Indices { 1508 if idx.State == perceptron.StatePublic { 1509 newIndices = append(newIndices, idx) 1510 } 1511 } 1512 tblInfo.DeferredCausets = newDeferredCausets 1513 tblInfo.Indices = newIndices 1514 tblInfo.Name = ident.Name 1515 tblInfo.AutoIncID = 0 1516 tblInfo.ForeignKeys = nil 1517 if tblInfo.TiFlashReplica != nil { 1518 replica := *tblInfo.TiFlashReplica 1519 // Keep the tiflash replica setting, remove the replica available status. 1520 replica.AvailablePartitionIDs = nil 1521 replica.Available = false 1522 tblInfo.TiFlashReplica = &replica 1523 } 1524 if referTblInfo.Partition != nil { 1525 pi := *referTblInfo.Partition 1526 pi.Definitions = make([]perceptron.PartitionDefinition, len(referTblInfo.Partition.Definitions)) 1527 copy(pi.Definitions, referTblInfo.Partition.Definitions) 1528 tblInfo.Partition = &pi 1529 } 1530 return &tblInfo, nil 1531 } 1532 1533 // BuildTableInfoFromAST builds perceptron.TableInfo from a ALLEGROALLEGROSQL memex. 1534 // Note: TableID and PartitionID are left as uninitialized value. 1535 func BuildTableInfoFromAST(s *ast.CreateTableStmt) (*perceptron.TableInfo, error) { 1536 return buildTableInfoWithCheck(mock.NewContext(), s, allegrosql.DefaultCharset, "") 1537 } 1538 1539 // buildTableInfoWithCheck builds perceptron.TableInfo from a ALLEGROALLEGROSQL memex. 1540 // Note: TableID and PartitionIDs are left as uninitialized value. 1541 func buildTableInfoWithCheck(ctx stochastikctx.Context, s *ast.CreateTableStmt, dbCharset, dbDefCauslate string) (*perceptron.TableInfo, error) { 1542 tbInfo, err := buildTableInfoWithStmt(ctx, s, dbCharset, dbDefCauslate) 1543 if err != nil { 1544 return nil, err 1545 } 1546 // Fix issue 17952 which will cause partition range expr can't be parsed as Int. 1547 // checkTableInfoValidWithStmt will do the constant fold the partition memex first, 1548 // then checkTableInfoValidExtra will pass the blockInfo check successfully. 1549 if err = checkTableInfoValidWithStmt(ctx, tbInfo, s); err != nil { 1550 return nil, err 1551 } 1552 if err = checkTableInfoValidExtra(tbInfo); err != nil { 1553 return nil, err 1554 } 1555 return tbInfo, nil 1556 } 1557 1558 // buildTableInfoWithStmt builds perceptron.TableInfo from a ALLEGROALLEGROSQL memex without validity check 1559 func buildTableInfoWithStmt(ctx stochastikctx.Context, s *ast.CreateTableStmt, dbCharset, dbDefCauslate string) (*perceptron.TableInfo, error) { 1560 defCausDefs := s.DefCauss 1561 blockCharset, blockDefCauslate, err := getCharsetAndDefCauslateInTableOption(0, s.Options) 1562 if err != nil { 1563 return nil, errors.Trace(err) 1564 } 1565 blockCharset, blockDefCauslate, err = ResolveCharsetDefCauslation( 1566 ast.CharsetOpt{Chs: blockCharset, DefCaus: blockDefCauslate}, 1567 ast.CharsetOpt{Chs: dbCharset, DefCaus: dbDefCauslate}, 1568 ) 1569 if err != nil { 1570 return nil, errors.Trace(err) 1571 } 1572 1573 // The defCausumn charset haven't been resolved here. 1574 defcaus, newConstraints, err := buildDeferredCausetsAndConstraints(ctx, defCausDefs, s.Constraints, blockCharset, blockDefCauslate) 1575 if err != nil { 1576 return nil, errors.Trace(err) 1577 } 1578 err = checkConstraintNames(newConstraints) 1579 if err != nil { 1580 return nil, errors.Trace(err) 1581 } 1582 1583 var tbInfo *perceptron.TableInfo 1584 tbInfo, err = buildTableInfo(ctx, s.Block.Name, defcaus, newConstraints, blockCharset, blockDefCauslate) 1585 if err != nil { 1586 return nil, errors.Trace(err) 1587 } 1588 1589 if err = setTableAutoRandomBits(ctx, tbInfo, defCausDefs); err != nil { 1590 return nil, errors.Trace(err) 1591 } 1592 1593 tbInfo.Partition, err = buildTablePartitionInfo(ctx, s) 1594 if err != nil { 1595 return nil, errors.Trace(err) 1596 } 1597 1598 if err = handleTableOptions(s.Options, tbInfo); err != nil { 1599 return nil, errors.Trace(err) 1600 } 1601 return tbInfo, nil 1602 } 1603 1604 func (d *dbs) assignTableID(tbInfo *perceptron.TableInfo) error { 1605 genIDs, err := d.genGlobalIDs(1) 1606 if err != nil { 1607 return errors.Trace(err) 1608 } 1609 tbInfo.ID = genIDs[0] 1610 return nil 1611 } 1612 1613 func (d *dbs) assignPartitionIDs(tbInfo *perceptron.TableInfo) error { 1614 if tbInfo.Partition == nil { 1615 return nil 1616 } 1617 partitionDefs := tbInfo.Partition.Definitions 1618 genIDs, err := d.genGlobalIDs(len(partitionDefs)) 1619 if err != nil { 1620 return errors.Trace(err) 1621 } 1622 for i := range partitionDefs { 1623 partitionDefs[i].ID = genIDs[i] 1624 } 1625 return nil 1626 } 1627 1628 func (d *dbs) CreateTable(ctx stochastikctx.Context, s *ast.CreateTableStmt) (err error) { 1629 ident := ast.Ident{Schema: s.Block.Schema, Name: s.Block.Name} 1630 is := d.GetSchemaReplicantWithInterceptor(ctx) 1631 schemaReplicant, ok := is.SchemaByName(ident.Schema) 1632 if !ok { 1633 return schemareplicant.ErrDatabaseNotExists.GenWithStackByArgs(ident.Schema) 1634 } 1635 1636 var referTbl causet.Block 1637 if s.ReferTable != nil { 1638 referIdent := ast.Ident{Schema: s.ReferTable.Schema, Name: s.ReferTable.Name} 1639 _, ok := is.SchemaByName(referIdent.Schema) 1640 if !ok { 1641 return schemareplicant.ErrTableNotExists.GenWithStackByArgs(referIdent.Schema, referIdent.Name) 1642 } 1643 referTbl, err = is.TableByName(referIdent.Schema, referIdent.Name) 1644 if err != nil { 1645 return schemareplicant.ErrTableNotExists.GenWithStackByArgs(referIdent.Schema, referIdent.Name) 1646 } 1647 } 1648 1649 // build blockInfo 1650 var tbInfo *perceptron.TableInfo 1651 if s.ReferTable != nil { 1652 tbInfo, err = buildTableInfoWithLike(ident, referTbl.Meta()) 1653 } else { 1654 tbInfo, err = buildTableInfoWithStmt(ctx, s, schemaReplicant.Charset, schemaReplicant.DefCauslate) 1655 } 1656 if err != nil { 1657 return errors.Trace(err) 1658 } 1659 1660 if err = checkTableInfoValidWithStmt(ctx, tbInfo, s); err != nil { 1661 return err 1662 } 1663 1664 onExist := OnExistError 1665 if s.IfNotExists { 1666 onExist = OnExistIgnore 1667 } 1668 1669 return d.CreateTableWithInfo(ctx, schemaReplicant.Name, tbInfo, onExist, false /*tryRetainID*/) 1670 } 1671 1672 func (d *dbs) CreateTableWithInfo( 1673 ctx stochastikctx.Context, 1674 dbName perceptron.CIStr, 1675 tbInfo *perceptron.TableInfo, 1676 onExist OnExist, 1677 tryRetainID bool, 1678 ) (err error) { 1679 is := d.GetSchemaReplicantWithInterceptor(ctx) 1680 schemaReplicant, ok := is.SchemaByName(dbName) 1681 if !ok { 1682 return schemareplicant.ErrDatabaseNotExists.GenWithStackByArgs(dbName) 1683 } 1684 1685 var oldViewTblID int64 1686 if oldTable, err := is.TableByName(schemaReplicant.Name, tbInfo.Name); err == nil { 1687 err = schemareplicant.ErrTableExists.GenWithStackByArgs(ast.Ident{Schema: schemaReplicant.Name, Name: tbInfo.Name}) 1688 switch onExist { 1689 case OnExistIgnore: 1690 ctx.GetStochastikVars().StmtCtx.AppendNote(err) 1691 return nil 1692 case OnExistReplace: 1693 // only CREATE OR REPLACE VIEW is supported at the moment. 1694 if tbInfo.View != nil { 1695 if oldTable.Meta().IsView() { 1696 oldViewTblID = oldTable.Meta().ID 1697 break 1698 } 1699 // The object to replace isn't a view. 1700 return ErrWrongObject.GenWithStackByArgs(dbName, tbInfo.Name, "VIEW") 1701 } 1702 return err 1703 default: 1704 return err 1705 } 1706 } 1707 1708 // FIXME: Implement `tryRetainID` 1709 if err := d.assignTableID(tbInfo); err != nil { 1710 return errors.Trace(err) 1711 } 1712 if err := d.assignPartitionIDs(tbInfo); err != nil { 1713 return errors.Trace(err) 1714 } 1715 1716 if err := checkTableInfoValidExtra(tbInfo); err != nil { 1717 return err 1718 } 1719 1720 var actionType perceptron.CausetActionType 1721 args := []interface{}{tbInfo} 1722 switch { 1723 case tbInfo.View != nil: 1724 actionType = perceptron.CausetActionCreateView 1725 args = append(args, onExist == OnExistReplace, oldViewTblID) 1726 case tbInfo.Sequence != nil: 1727 actionType = perceptron.CausetActionCreateSequence 1728 default: 1729 actionType = perceptron.CausetActionCreateTable 1730 } 1731 1732 job := &perceptron.Job{ 1733 SchemaID: schemaReplicant.ID, 1734 TableID: tbInfo.ID, 1735 SchemaName: schemaReplicant.Name.L, 1736 Type: actionType, 1737 BinlogInfo: &perceptron.HistoryInfo{}, 1738 Args: args, 1739 } 1740 1741 err = d.doDBSJob(ctx, job) 1742 if err != nil { 1743 // causet exists, but if_not_exists flags is true, so we ignore this error. 1744 if onExist == OnExistIgnore && schemareplicant.ErrTableExists.Equal(err) { 1745 ctx.GetStochastikVars().StmtCtx.AppendNote(err) 1746 err = nil 1747 } 1748 } else if actionType == perceptron.CausetActionCreateTable { 1749 d.preSplitAndScatter(ctx, tbInfo, tbInfo.GetPartitionInfo()) 1750 if tbInfo.AutoIncID > 1 { 1751 // Default blockAutoIncID base is 0. 1752 // If the first ID is expected to greater than 1, we need to do rebase. 1753 newEnd := tbInfo.AutoIncID - 1 1754 if err = d.handleAutoIncID(tbInfo, schemaReplicant.ID, newEnd, autoid.RowIDAllocType); err != nil { 1755 return errors.Trace(err) 1756 } 1757 } 1758 if tbInfo.AutoRandID > 1 { 1759 // Default blockAutoRandID base is 0. 1760 // If the first ID is expected to greater than 1, we need to do rebase. 1761 newEnd := tbInfo.AutoRandID - 1 1762 err = d.handleAutoIncID(tbInfo, schemaReplicant.ID, newEnd, autoid.AutoRandomType) 1763 } 1764 } 1765 1766 err = d.callHookOnChanged(err) 1767 return errors.Trace(err) 1768 } 1769 1770 // preSplitAndScatter performs pre-split and scatter of the causet's regions. 1771 // If `pi` is not nil, will only split region for `pi`, this is used when add partition. 1772 func (d *dbs) preSplitAndScatter(ctx stochastikctx.Context, tbInfo *perceptron.TableInfo, pi *perceptron.PartitionInfo) { 1773 sp, ok := d.causetstore.(ekv.SplitblockStore) 1774 if !ok || atomic.LoadUint32(&EnableSplitTableRegion) == 0 { 1775 return 1776 } 1777 var ( 1778 preSplit func() 1779 scatterRegion bool 1780 ) 1781 val, err := variable.GetGlobalSystemVar(ctx.GetStochastikVars(), variable.MilevaDBScatterRegion) 1782 if err != nil { 1783 logutil.BgLogger().Warn("[dbs] won't scatter region", zap.Error(err)) 1784 } else { 1785 scatterRegion = variable.MilevaDBOptOn(val) 1786 } 1787 if pi != nil { 1788 preSplit = func() { splitPartitionTableRegion(ctx, sp, tbInfo, pi, scatterRegion) } 1789 } else { 1790 preSplit = func() { splitTableRegion(ctx, sp, tbInfo, scatterRegion) } 1791 } 1792 if scatterRegion { 1793 preSplit() 1794 } else { 1795 go preSplit() 1796 } 1797 } 1798 1799 func (d *dbs) RecoverTable(ctx stochastikctx.Context, recoverInfo *RecoverInfo) (err error) { 1800 is := d.GetSchemaReplicantWithInterceptor(ctx) 1801 schemaID, tbInfo := recoverInfo.SchemaID, recoverInfo.TableInfo 1802 // Check schemaReplicant exist. 1803 schemaReplicant, ok := is.SchemaByID(schemaID) 1804 if !ok { 1805 return errors.Trace(schemareplicant.ErrDatabaseNotExists.GenWithStackByArgs( 1806 fmt.Sprintf("(Schema ID %d)", schemaID), 1807 )) 1808 } 1809 // Check not exist causet with same name. 1810 if ok := is.TableExists(schemaReplicant.Name, tbInfo.Name); ok { 1811 return schemareplicant.ErrTableExists.GenWithStackByArgs(tbInfo.Name) 1812 } 1813 1814 tbInfo.State = perceptron.StateNone 1815 job := &perceptron.Job{ 1816 SchemaID: schemaID, 1817 TableID: tbInfo.ID, 1818 SchemaName: schemaReplicant.Name.L, 1819 Type: perceptron.CausetActionRecoverTable, 1820 BinlogInfo: &perceptron.HistoryInfo{}, 1821 Args: []interface{}{tbInfo, recoverInfo.CurAutoIncID, recoverInfo.DropJobID, 1822 recoverInfo.SnapshotTS, recoverTableCheckFlagNone, recoverInfo.CurAutoRandID}, 1823 } 1824 err = d.doDBSJob(ctx, job) 1825 err = d.callHookOnChanged(err) 1826 return errors.Trace(err) 1827 } 1828 1829 func (d *dbs) CreateView(ctx stochastikctx.Context, s *ast.CreateViewStmt) (err error) { 1830 viewInfo, err := buildViewInfo(ctx, s) 1831 if err != nil { 1832 return err 1833 } 1834 1835 defcaus := make([]*causet.DeferredCauset, len(s.DefCauss)) 1836 for i, v := range s.DefCauss { 1837 defcaus[i] = causet.ToDeferredCauset(&perceptron.DeferredCausetInfo{ 1838 Name: v, 1839 ID: int64(i), 1840 Offset: i, 1841 State: perceptron.StatePublic, 1842 }) 1843 } 1844 1845 tblCharset := "" 1846 tblDefCauslate := "" 1847 if v, ok := ctx.GetStochastikVars().GetSystemVar("character_set_client"); ok { 1848 tblCharset = v 1849 } 1850 if v, ok := ctx.GetStochastikVars().GetSystemVar("defCauslation_connection"); ok { 1851 tblDefCauslate = v 1852 } 1853 1854 tbInfo, err := buildTableInfo(ctx, s.ViewName.Name, defcaus, nil, tblCharset, tblDefCauslate) 1855 if err != nil { 1856 return err 1857 } 1858 tbInfo.View = viewInfo 1859 1860 onExist := OnExistError 1861 if s.OrReplace { 1862 onExist = OnExistReplace 1863 } 1864 1865 return d.CreateTableWithInfo(ctx, s.ViewName.Schema, tbInfo, onExist, false /*tryRetainID*/) 1866 } 1867 1868 func buildViewInfo(ctx stochastikctx.Context, s *ast.CreateViewStmt) (*perceptron.ViewInfo, error) { 1869 // Always Use `format.RestoreNameBackQuotes` to restore `SELECT` memex despite the `ANSI_QUOTES` ALLEGROALLEGROSQL Mode is enabled or not. 1870 restoreFlag := format.RestoreStringSingleQuotes | format.RestoreKeyWordUppercase | format.RestoreNameBackQuotes 1871 var sb strings.Builder 1872 if err := s.Select.Restore(format.NewRestoreCtx(restoreFlag, &sb)); err != nil { 1873 return nil, err 1874 } 1875 1876 return &perceptron.ViewInfo{Definer: s.Definer, Algorithm: s.Algorithm, 1877 Security: s.Security, SelectStmt: sb.String(), CheckOption: s.CheckOption, DefCauss: nil}, nil 1878 } 1879 1880 func checkPartitionByHash(ctx stochastikctx.Context, tbInfo *perceptron.TableInfo, s *ast.CreateTableStmt) error { 1881 pi := tbInfo.Partition 1882 if err := checkAddPartitionTooManyPartitions(pi.Num); err != nil { 1883 return err 1884 } 1885 if err := checkNoHashPartitions(ctx, pi.Num); err != nil { 1886 return err 1887 } 1888 if err := checkPartitionFuncValid(ctx, tbInfo, s.Partition.Expr); err != nil { 1889 return err 1890 } 1891 return checkPartitionFuncType(ctx, s, tbInfo) 1892 } 1893 1894 // checkPartitionByRange checks validity of a "BY RANGE" partition. 1895 func checkPartitionByRange(ctx stochastikctx.Context, tbInfo *perceptron.TableInfo, s *ast.CreateTableStmt) error { 1896 pi := tbInfo.Partition 1897 if err := checkPartitionNameUnique(pi); err != nil { 1898 return err 1899 } 1900 1901 if err := checkAddPartitionTooManyPartitions(uint64(len(pi.Definitions))); err != nil { 1902 return err 1903 } 1904 1905 if err := checkNoRangePartitions(len(pi.Definitions)); err != nil { 1906 return err 1907 } 1908 1909 if len(pi.DeferredCausets) == 0 { 1910 if err := checkCreatePartitionValue(ctx, tbInfo); err != nil { 1911 return err 1912 } 1913 1914 // s maybe nil when add partition. 1915 if s == nil { 1916 return nil 1917 } 1918 1919 if err := checkPartitionFuncValid(ctx, tbInfo, s.Partition.Expr); err != nil { 1920 return err 1921 } 1922 return checkPartitionFuncType(ctx, s, tbInfo) 1923 } 1924 1925 // Check for range defCausumns partition. 1926 if err := checkRangeDeferredCausetsPartitionType(tbInfo); err != nil { 1927 return err 1928 } 1929 1930 if s != nil { 1931 for _, def := range s.Partition.Definitions { 1932 exprs := def.Clause.(*ast.PartitionDefinitionClauseLessThan).Exprs 1933 if err := checkRangeDeferredCausetsTypeAndValuesMatch(ctx, tbInfo, exprs); err != nil { 1934 return err 1935 } 1936 } 1937 } 1938 1939 return checkRangeDeferredCausetsPartitionValue(ctx, tbInfo) 1940 } 1941 1942 func checkRangeDeferredCausetsPartitionType(tbInfo *perceptron.TableInfo) error { 1943 for _, defCaus := range tbInfo.Partition.DeferredCausets { 1944 defCausInfo := getDeferredCausetInfoByName(tbInfo, defCaus.L) 1945 if defCausInfo == nil { 1946 return errors.Trace(ErrFieldNotFoundPart) 1947 } 1948 // The permitted data types are shown in the following list: 1949 // All integer types 1950 // DATE and DATETIME 1951 // CHAR, VARCHAR, BINARY, and VARBINARY 1952 // See https://dev.allegrosql.com/doc/allegrosql-partitioning-excerpt/5.7/en/partitioning-defCausumns.html 1953 switch defCausInfo.FieldType.Tp { 1954 case allegrosql.TypeTiny, allegrosql.TypeShort, allegrosql.TypeInt24, allegrosql.TypeLong, allegrosql.TypeLonglong: 1955 case allegrosql.TypeDate, allegrosql.TypeDatetime: 1956 case allegrosql.TypeVarchar, allegrosql.TypeString: 1957 default: 1958 return ErrNotAllowedTypeInPartition.GenWithStackByArgs(defCaus.O) 1959 } 1960 } 1961 return nil 1962 } 1963 1964 func checkRangeDeferredCausetsPartitionValue(ctx stochastikctx.Context, tbInfo *perceptron.TableInfo) error { 1965 // Range defCausumns partition key supports multiple data types with integer、datetime、string. 1966 pi := tbInfo.Partition 1967 defs := pi.Definitions 1968 if len(defs) < 1 { 1969 return ast.ErrPartitionsMustBeDefined.GenWithStackByArgs("RANGE") 1970 } 1971 1972 curr := &defs[0] 1973 if len(curr.LessThan) != len(pi.DeferredCausets) { 1974 return errors.Trace(ast.ErrPartitionDeferredCausetList) 1975 } 1976 var prev *perceptron.PartitionDefinition 1977 for i := 1; i < len(defs); i++ { 1978 prev, curr = curr, &defs[i] 1979 succ, err := checkTwoRangeDeferredCausets(ctx, curr, prev, pi, tbInfo) 1980 if err != nil { 1981 return err 1982 } 1983 if !succ { 1984 return errors.Trace(ErrRangeNotIncreasing) 1985 } 1986 } 1987 return nil 1988 } 1989 1990 func checkTwoRangeDeferredCausets(ctx stochastikctx.Context, curr, prev *perceptron.PartitionDefinition, pi *perceptron.PartitionInfo, tbInfo *perceptron.TableInfo) (bool, error) { 1991 if len(curr.LessThan) != len(pi.DeferredCausets) { 1992 return false, errors.Trace(ast.ErrPartitionDeferredCausetList) 1993 } 1994 for i := 0; i < len(pi.DeferredCausets); i++ { 1995 // Special handling for MAXVALUE. 1996 if strings.EqualFold(curr.LessThan[i], partitionMaxValue) { 1997 // If current is maxvalue, it certainly >= previous. 1998 return true, nil 1999 } 2000 if strings.EqualFold(prev.LessThan[i], partitionMaxValue) { 2001 // Current is not maxvalue, and previous is maxvalue. 2002 return false, nil 2003 } 2004 2005 // Current and previous is the same. 2006 if strings.EqualFold(curr.LessThan[i], prev.LessThan[i]) { 2007 continue 2008 } 2009 2010 // The tuples of defCausumn values used to define the partitions are strictly increasing: 2011 // PARTITION p0 VALUES LESS THAN (5,10,'ggg') 2012 // PARTITION p1 VALUES LESS THAN (10,20,'mmm') 2013 // PARTITION p2 VALUES LESS THAN (15,30,'sss') 2014 succ, err := parseAndEvalBoolExpr(ctx, fmt.Sprintf("(%s) > (%s)", curr.LessThan[i], prev.LessThan[i]), tbInfo) 2015 if err != nil { 2016 return false, err 2017 } 2018 2019 if succ { 2020 return true, nil 2021 } 2022 } 2023 return false, nil 2024 } 2025 2026 func parseAndEvalBoolExpr(ctx stochastikctx.Context, expr string, tbInfo *perceptron.TableInfo) (bool, error) { 2027 e, err := memex.ParseSimpleExprWithTableInfo(ctx, expr, tbInfo) 2028 if err != nil { 2029 return false, err 2030 } 2031 res, _, err1 := e.EvalInt(ctx, chunk.Row{}) 2032 if err1 != nil { 2033 return false, err1 2034 } 2035 return res > 0, nil 2036 } 2037 2038 func checkCharsetAndDefCauslation(cs string, co string) error { 2039 if !charset.ValidCharsetAndDefCauslation(cs, co) { 2040 return ErrUnknownCharacterSet.GenWithStackByArgs(cs) 2041 } 2042 if co != "" { 2043 if _, err := defCauslate.GetDefCauslationByName(co); err != nil { 2044 return errors.Trace(err) 2045 } 2046 } 2047 return nil 2048 } 2049 2050 // handleAutoIncID handles auto_increment option in DBS. It creates a ID counter for the causet and initiates the counter to a proper value. 2051 // For example if the option sets auto_increment to 10. The counter will be set to 9. So the next allocated ID will be 10. 2052 func (d *dbs) handleAutoIncID(tbInfo *perceptron.TableInfo, schemaID int64, newEnd int64, tp autoid.SlabPredictorType) error { 2053 allocs := autoid.NewSlabPredictorsFromTblInfo(d.causetstore, schemaID, tbInfo) 2054 if alloc := allocs.Get(tp); alloc != nil { 2055 err := alloc.Rebase(tbInfo.ID, newEnd, false) 2056 if err != nil { 2057 return errors.Trace(err) 2058 } 2059 } 2060 return nil 2061 } 2062 2063 // handleTableOptions uFIDelates blockInfo according to causet options. 2064 func handleTableOptions(options []*ast.TableOption, tbInfo *perceptron.TableInfo) error { 2065 for _, op := range options { 2066 switch op.Tp { 2067 case ast.TableOptionAutoIncrement: 2068 tbInfo.AutoIncID = int64(op.UintValue) 2069 case ast.TableOptionAutoIdCache: 2070 if op.UintValue > uint64(math.MaxInt64) { 2071 // TODO: Refine this error. 2072 return errors.New("causet option auto_id_cache overflows int64") 2073 } 2074 tbInfo.AutoIdCache = int64(op.UintValue) 2075 case ast.TableOptionAutoRandomBase: 2076 tbInfo.AutoRandID = int64(op.UintValue) 2077 case ast.TableOptionComment: 2078 tbInfo.Comment = op.StrValue 2079 case ast.TableOptionCompression: 2080 tbInfo.Compression = op.StrValue 2081 case ast.TableOptionShardRowID: 2082 if op.UintValue > 0 && tbInfo.PKIsHandle { 2083 return errUnsupportedShardRowIDBits 2084 } 2085 tbInfo.ShardRowIDBits = op.UintValue 2086 if tbInfo.ShardRowIDBits > shardRowIDBitsMax { 2087 tbInfo.ShardRowIDBits = shardRowIDBitsMax 2088 } 2089 tbInfo.MaxShardRowIDBits = tbInfo.ShardRowIDBits 2090 case ast.TableOptionPreSplitRegion: 2091 tbInfo.PreSplitRegions = op.UintValue 2092 case ast.TableOptionCharset, ast.TableOptionDefCauslate: 2093 // We don't handle charset and defCauslate here since they're handled in `getCharsetAndDefCauslateInTableOption`. 2094 } 2095 } 2096 if tbInfo.PreSplitRegions > tbInfo.ShardRowIDBits { 2097 tbInfo.PreSplitRegions = tbInfo.ShardRowIDBits 2098 } 2099 return nil 2100 } 2101 2102 // isIgnorableSpec checks if the spec type is ignorable. 2103 // Some specs are parsed by ignored. This is for compatibility. 2104 func isIgnorableSpec(tp ast.AlterTableType) bool { 2105 // AlterTableLock/AlterTableAlgorithm are ignored. 2106 return tp == ast.AlterTableLock || tp == ast.AlterTableAlgorithm 2107 } 2108 2109 // getCharsetAndDefCauslateInDeferredCausetDef will iterate defCauslate in the options, validate it by checking the charset 2110 // of defCausumn definition. If there's no defCauslate in the option, the default defCauslate of defCausumn's charset will be used. 2111 func getCharsetAndDefCauslateInDeferredCausetDef(def *ast.DeferredCausetDef) (chs, defCausl string, err error) { 2112 chs = def.Tp.Charset 2113 defCausl = def.Tp.DefCauslate 2114 if chs != "" && defCausl == "" { 2115 if defCausl, err = charset.GetDefaultDefCauslation(chs); err != nil { 2116 return "", "", errors.Trace(err) 2117 } 2118 } 2119 for _, opt := range def.Options { 2120 if opt.Tp == ast.DeferredCausetOptionDefCauslate { 2121 info, err := defCauslate.GetDefCauslationByName(opt.StrValue) 2122 if err != nil { 2123 return "", "", errors.Trace(err) 2124 } 2125 if chs == "" { 2126 chs = info.CharsetName 2127 } else if chs != info.CharsetName { 2128 return "", "", ErrDefCauslationCharsetMismatch.GenWithStackByArgs(info.Name, chs) 2129 } 2130 defCausl = info.Name 2131 } 2132 } 2133 return 2134 } 2135 2136 // getCharsetAndDefCauslateInTableOption will iterate the charset and defCauslate in the options, 2137 // and returns the last charset and defCauslate in options. If there is no charset in the options, 2138 // the returns charset will be "", the same as defCauslate. 2139 func getCharsetAndDefCauslateInTableOption(startIdx int, options []*ast.TableOption) (chs, defCausl string, err error) { 2140 for i := startIdx; i < len(options); i++ { 2141 opt := options[i] 2142 // we set the charset to the last option. example: alter causet t charset latin1 charset utf8 defCauslate utf8_bin; 2143 // the charset will be utf8, defCauslate will be utf8_bin 2144 switch opt.Tp { 2145 case ast.TableOptionCharset: 2146 info, err := charset.GetCharsetDesc(opt.StrValue) 2147 if err != nil { 2148 return "", "", err 2149 } 2150 if len(chs) == 0 { 2151 chs = info.Name 2152 } else if chs != info.Name { 2153 return "", "", ErrConflictingDeclarations.GenWithStackByArgs(chs, info.Name) 2154 } 2155 if len(defCausl) == 0 { 2156 defCausl = info.DefaultDefCauslation 2157 } 2158 case ast.TableOptionDefCauslate: 2159 info, err := defCauslate.GetDefCauslationByName(opt.StrValue) 2160 if err != nil { 2161 return "", "", err 2162 } 2163 if len(chs) == 0 { 2164 chs = info.CharsetName 2165 } else if chs != info.CharsetName { 2166 return "", "", ErrDefCauslationCharsetMismatch.GenWithStackByArgs(info.Name, chs) 2167 } 2168 defCausl = info.Name 2169 } 2170 } 2171 return 2172 } 2173 2174 func needToOverwriteDefCausCharset(options []*ast.TableOption) bool { 2175 for i := len(options) - 1; i >= 0; i-- { 2176 opt := options[i] 2177 switch opt.Tp { 2178 case ast.TableOptionCharset: 2179 // Only overwrite defCausumns charset if the option contains `CONVERT TO`. 2180 return opt.UintValue == ast.TableOptionCharsetWithConvertTo 2181 } 2182 } 2183 return false 2184 } 2185 2186 // resolveAlterTableSpec resolves alter causet algorithm and removes ignore causet spec in specs. 2187 // returns valied specs, and the occurred error. 2188 func resolveAlterTableSpec(ctx stochastikctx.Context, specs []*ast.AlterTableSpec) ([]*ast.AlterTableSpec, error) { 2189 validSpecs := make([]*ast.AlterTableSpec, 0, len(specs)) 2190 algorithm := ast.AlgorithmTypeDefault 2191 for _, spec := range specs { 2192 if spec.Tp == ast.AlterTableAlgorithm { 2193 // Find the last AlterTableAlgorithm. 2194 algorithm = spec.Algorithm 2195 } 2196 if isIgnorableSpec(spec.Tp) { 2197 continue 2198 } 2199 validSpecs = append(validSpecs, spec) 2200 } 2201 2202 // Verify whether the algorithm is supported. 2203 for _, spec := range validSpecs { 2204 resolvedAlgorithm, err := ResolveAlterAlgorithm(spec, algorithm) 2205 if err != nil { 2206 // If MilevaDB failed to choose a better algorithm, report the error 2207 if resolvedAlgorithm == ast.AlgorithmTypeDefault { 2208 return nil, errors.Trace(err) 2209 } 2210 // For the compatibility, we return warning instead of error when a better algorithm is chosed by MilevaDB 2211 ctx.GetStochastikVars().StmtCtx.AppendError(err) 2212 } 2213 2214 spec.Algorithm = resolvedAlgorithm 2215 } 2216 2217 // Only handle valid specs. 2218 return validSpecs, nil 2219 } 2220 2221 func isSameTypeMultiSpecs(specs []*ast.AlterTableSpec) bool { 2222 specType := specs[0].Tp 2223 for _, spec := range specs { 2224 if spec.Tp != specType { 2225 return false 2226 } 2227 } 2228 return true 2229 } 2230 2231 func (d *dbs) AlterTable(ctx stochastikctx.Context, ident ast.Ident, specs []*ast.AlterTableSpec) (err error) { 2232 validSpecs, err := resolveAlterTableSpec(ctx, specs) 2233 if err != nil { 2234 return errors.Trace(err) 2235 } 2236 2237 is := d.infoHandle.Get() 2238 if is.TableIsView(ident.Schema, ident.Name) || is.TableIsSequence(ident.Schema, ident.Name) { 2239 return ErrWrongObject.GenWithStackByArgs(ident.Schema, ident.Name, "BASE TABLE") 2240 } 2241 2242 if len(validSpecs) > 1 { 2243 if isSameTypeMultiSpecs(validSpecs) { 2244 switch validSpecs[0].Tp { 2245 case ast.AlterTableAddDeferredCausets: 2246 err = d.AddDeferredCausets(ctx, ident, validSpecs) 2247 case ast.AlterTableDropDeferredCauset: 2248 err = d.DropDeferredCausets(ctx, ident, validSpecs) 2249 default: 2250 return errRunMultiSchemaChanges 2251 } 2252 if err != nil { 2253 return errors.Trace(err) 2254 } 2255 return nil 2256 } 2257 return errRunMultiSchemaChanges 2258 } 2259 2260 for _, spec := range validSpecs { 2261 var handledCharsetOrDefCauslate bool 2262 switch spec.Tp { 2263 case ast.AlterTableAddDeferredCausets: 2264 if len(spec.NewDeferredCausets) != 1 { 2265 err = d.AddDeferredCausets(ctx, ident, []*ast.AlterTableSpec{spec}) 2266 } else { 2267 err = d.AddDeferredCauset(ctx, ident, spec) 2268 } 2269 case ast.AlterTableAddPartitions: 2270 err = d.AddTablePartitions(ctx, ident, spec) 2271 case ast.AlterTableCoalescePartitions: 2272 err = d.CoalescePartitions(ctx, ident, spec) 2273 case ast.AlterTableReorganizePartition: 2274 err = errors.Trace(errUnsupportedReorganizePartition) 2275 case ast.AlterTableCheckPartitions: 2276 err = errors.Trace(errUnsupportedCheckPartition) 2277 case ast.AlterTableRebuildPartition: 2278 err = errors.Trace(errUnsupportedRebuildPartition) 2279 case ast.AlterTableOptimizePartition: 2280 err = errors.Trace(errUnsupportedOptimizePartition) 2281 case ast.AlterTableRemovePartitioning: 2282 err = errors.Trace(errUnsupportedRemovePartition) 2283 case ast.AlterTableRepairPartition: 2284 err = errors.Trace(errUnsupportedRepairPartition) 2285 case ast.AlterTableDropDeferredCauset: 2286 err = d.DropDeferredCauset(ctx, ident, spec) 2287 case ast.AlterTableDropIndex: 2288 err = d.DropIndex(ctx, ident, perceptron.NewCIStr(spec.Name), spec.IfExists) 2289 case ast.AlterTableDropPrimaryKey: 2290 err = d.DropIndex(ctx, ident, perceptron.NewCIStr(allegrosql.PrimaryKeyName), spec.IfExists) 2291 case ast.AlterTableRenameIndex: 2292 err = d.RenameIndex(ctx, ident, spec) 2293 case ast.AlterTableDropPartition: 2294 err = d.DropTablePartition(ctx, ident, spec) 2295 case ast.AlterTableTruncatePartition: 2296 err = d.TruncateTablePartition(ctx, ident, spec) 2297 case ast.AlterTableExchangePartition: 2298 err = d.ExchangeTablePartition(ctx, ident, spec) 2299 case ast.AlterTableAddConstraint: 2300 constr := spec.Constraint 2301 switch spec.Constraint.Tp { 2302 case ast.ConstraintKey, ast.ConstraintIndex: 2303 err = d.CreateIndex(ctx, ident, ast.IndexKeyTypeNone, perceptron.NewCIStr(constr.Name), 2304 spec.Constraint.Keys, constr.Option, constr.IfNotExists) 2305 case ast.ConstraintUniq, ast.ConstraintUniqIndex, ast.ConstraintUniqKey: 2306 err = d.CreateIndex(ctx, ident, ast.IndexKeyTypeUnique, perceptron.NewCIStr(constr.Name), 2307 spec.Constraint.Keys, constr.Option, false) // IfNotExists should be not applied 2308 case ast.ConstraintForeignKey: 2309 // NOTE: we do not handle `symbol` and `index_name` well in the BerolinaSQL and we do not check ForeignKey already exists, 2310 // so we just also ignore the `if not exists` check. 2311 err = d.CreateForeignKey(ctx, ident, perceptron.NewCIStr(constr.Name), spec.Constraint.Keys, spec.Constraint.Refer) 2312 case ast.ConstraintPrimaryKey: 2313 err = d.CreatePrimaryKey(ctx, ident, perceptron.NewCIStr(constr.Name), spec.Constraint.Keys, constr.Option) 2314 case ast.ConstraintFulltext: 2315 ctx.GetStochastikVars().StmtCtx.AppendWarning(ErrTableCantHandleFt) 2316 case ast.ConstraintCheck: 2317 ctx.GetStochastikVars().StmtCtx.AppendWarning(ErrUnsupportedConstraintCheck.GenWithStackByArgs("ADD CONSTRAINT CHECK")) 2318 default: 2319 // Nothing to do now. 2320 } 2321 case ast.AlterTableDropForeignKey: 2322 // NOTE: we do not check `if not exists` and `if exists` for ForeignKey now. 2323 err = d.DropForeignKey(ctx, ident, perceptron.NewCIStr(spec.Name)) 2324 case ast.AlterTableModifyDeferredCauset: 2325 err = d.ModifyDeferredCauset(ctx, ident, spec) 2326 case ast.AlterTableChangeDeferredCauset: 2327 err = d.ChangeDeferredCauset(ctx, ident, spec) 2328 case ast.AlterTableRenameDeferredCauset: 2329 err = d.RenameDeferredCauset(ctx, ident, spec) 2330 case ast.AlterTableAlterDeferredCauset: 2331 err = d.AlterDeferredCauset(ctx, ident, spec) 2332 case ast.AlterTableRenameTable: 2333 newIdent := ast.Ident{Schema: spec.NewTable.Schema, Name: spec.NewTable.Name} 2334 isAlterTable := true 2335 err = d.RenameTable(ctx, ident, newIdent, isAlterTable) 2336 case ast.AlterTableAlterPartition: 2337 err = d.AlterTablePartition(ctx, ident, spec) 2338 case ast.AlterTablePartition: 2339 // Prevent silent succeed if user executes ALTER TABLE x PARTITION BY ... 2340 err = errors.New("alter causet partition is unsupported") 2341 case ast.AlterTableOption: 2342 for i, opt := range spec.Options { 2343 switch opt.Tp { 2344 case ast.TableOptionShardRowID: 2345 if opt.UintValue > shardRowIDBitsMax { 2346 opt.UintValue = shardRowIDBitsMax 2347 } 2348 err = d.ShardRowID(ctx, ident, opt.UintValue) 2349 case ast.TableOptionAutoIncrement: 2350 err = d.RebaseAutoID(ctx, ident, int64(opt.UintValue), autoid.RowIDAllocType) 2351 case ast.TableOptionAutoIdCache: 2352 if opt.UintValue > uint64(math.MaxInt64) { 2353 // TODO: Refine this error. 2354 return errors.New("causet option auto_id_cache overflows int64") 2355 } 2356 err = d.AlterTableAutoIDCache(ctx, ident, int64(opt.UintValue)) 2357 case ast.TableOptionAutoRandomBase: 2358 err = d.RebaseAutoID(ctx, ident, int64(opt.UintValue), autoid.AutoRandomType) 2359 case ast.TableOptionComment: 2360 spec.Comment = opt.StrValue 2361 err = d.AlterTableComment(ctx, ident, spec) 2362 case ast.TableOptionCharset, ast.TableOptionDefCauslate: 2363 // getCharsetAndDefCauslateInTableOption will get the last charset and defCauslate in the options, 2364 // so it should be handled only once. 2365 if handledCharsetOrDefCauslate { 2366 continue 2367 } 2368 var toCharset, toDefCauslate string 2369 toCharset, toDefCauslate, err = getCharsetAndDefCauslateInTableOption(i, spec.Options) 2370 if err != nil { 2371 return err 2372 } 2373 needsOverwriteDefCauss := needToOverwriteDefCausCharset(spec.Options) 2374 err = d.AlterTableCharsetAndDefCauslate(ctx, ident, toCharset, toDefCauslate, needsOverwriteDefCauss) 2375 handledCharsetOrDefCauslate = true 2376 } 2377 2378 if err != nil { 2379 return errors.Trace(err) 2380 } 2381 } 2382 case ast.AlterTableSetTiFlashReplica: 2383 err = d.AlterTableSetTiFlashReplica(ctx, ident, spec.TiFlashReplica) 2384 case ast.AlterTableOrderByDeferredCausets: 2385 err = d.OrderByDeferredCausets(ctx, ident) 2386 case ast.AlterTableIndexInvisible: 2387 err = d.AlterIndexVisibility(ctx, ident, spec.IndexName, spec.Visibility) 2388 case ast.AlterTableAlterCheck: 2389 ctx.GetStochastikVars().StmtCtx.AppendWarning(ErrUnsupportedConstraintCheck.GenWithStackByArgs("ALTER CHECK")) 2390 case ast.AlterTableDropCheck: 2391 ctx.GetStochastikVars().StmtCtx.AppendWarning(ErrUnsupportedConstraintCheck.GenWithStackByArgs("DROP CHECK")) 2392 case ast.AlterTableWithValidation: 2393 ctx.GetStochastikVars().StmtCtx.AppendWarning(errUnsupportedAlterTableWithValidation) 2394 case ast.AlterTableWithoutValidation: 2395 ctx.GetStochastikVars().StmtCtx.AppendWarning(errUnsupportedAlterTableWithoutValidation) 2396 default: 2397 // Nothing to do now. 2398 } 2399 2400 if err != nil { 2401 return errors.Trace(err) 2402 } 2403 } 2404 2405 return nil 2406 } 2407 2408 func (d *dbs) RebaseAutoID(ctx stochastikctx.Context, ident ast.Ident, newBase int64, tp autoid.SlabPredictorType) error { 2409 schemaReplicant, t, err := d.getSchemaAndTableByIdent(ctx, ident) 2410 if err != nil { 2411 return errors.Trace(err) 2412 } 2413 var actionType perceptron.CausetActionType 2414 switch tp { 2415 case autoid.AutoRandomType: 2416 tbInfo := t.Meta() 2417 if tbInfo.AutoRandomBits == 0 { 2418 return errors.Trace(ErrInvalidAutoRandom.GenWithStackByArgs(autoid.AutoRandomRebaseNotApplicable)) 2419 } 2420 var autoRandDefCausTp types.FieldType 2421 for _, c := range tbInfo.DeferredCausets { 2422 if allegrosql.HasPriKeyFlag(c.Flag) { 2423 autoRandDefCausTp = c.FieldType 2424 break 2425 } 2426 } 2427 layout := autoid.NewAutoRandomIDLayout(&autoRandDefCausTp, tbInfo.AutoRandomBits) 2428 if layout.IncrementalMask()&newBase != newBase { 2429 errMsg := fmt.Sprintf(autoid.AutoRandomRebaseOverflow, newBase, layout.IncrementalBitsCapacity()) 2430 return errors.Trace(ErrInvalidAutoRandom.GenWithStackByArgs(errMsg)) 2431 } 2432 actionType = perceptron.CausetActionRebaseAutoRandomBase 2433 case autoid.RowIDAllocType: 2434 actionType = perceptron.CausetActionRebaseAutoID 2435 } 2436 2437 if alloc := t.SlabPredictors(ctx).Get(tp); alloc != nil { 2438 autoID, err := alloc.NextGlobalAutoID(t.Meta().ID) 2439 if err != nil { 2440 return errors.Trace(err) 2441 } 2442 // If newBase < autoID, we need to do a rebase before returning. 2443 // Assume there are 2 MilevaDB servers: MilevaDB-A with allocator range of 0 ~ 30000; MilevaDB-B with allocator range of 30001 ~ 60000. 2444 // If the user sends ALLEGROALLEGROSQL `alter causet t1 auto_increment = 100` to MilevaDB-B, 2445 // and MilevaDB-B finds 100 < 30001 but returns without any handling, 2446 // then MilevaDB-A may still allocate 99 for auto_increment defCausumn. This doesn't make sense for the user. 2447 newBase = int64(mathutil.MaxUint64(uint64(newBase), uint64(autoID))) 2448 } 2449 job := &perceptron.Job{ 2450 SchemaID: schemaReplicant.ID, 2451 TableID: t.Meta().ID, 2452 SchemaName: schemaReplicant.Name.L, 2453 Type: actionType, 2454 BinlogInfo: &perceptron.HistoryInfo{}, 2455 Args: []interface{}{newBase}, 2456 } 2457 err = d.doDBSJob(ctx, job) 2458 err = d.callHookOnChanged(err) 2459 return errors.Trace(err) 2460 } 2461 2462 // ShardRowID shards the implicit event ID by adding shard value to the event ID's first few bits. 2463 func (d *dbs) ShardRowID(ctx stochastikctx.Context, blockIdent ast.Ident, uVal uint64) error { 2464 schemaReplicant, t, err := d.getSchemaAndTableByIdent(ctx, blockIdent) 2465 if err != nil { 2466 return errors.Trace(err) 2467 } 2468 if uVal == t.Meta().ShardRowIDBits { 2469 // Nothing need to do. 2470 return nil 2471 } 2472 if uVal > 0 && t.Meta().PKIsHandle { 2473 return errUnsupportedShardRowIDBits 2474 } 2475 err = verifyNoOverflowShardBits(d.sessPool, t, uVal) 2476 if err != nil { 2477 return err 2478 } 2479 job := &perceptron.Job{ 2480 Type: perceptron.CausetActionShardRowID, 2481 SchemaID: schemaReplicant.ID, 2482 TableID: t.Meta().ID, 2483 SchemaName: schemaReplicant.Name.L, 2484 BinlogInfo: &perceptron.HistoryInfo{}, 2485 Args: []interface{}{uVal}, 2486 } 2487 err = d.doDBSJob(ctx, job) 2488 err = d.callHookOnChanged(err) 2489 return errors.Trace(err) 2490 } 2491 2492 func (d *dbs) getSchemaAndTableByIdent(ctx stochastikctx.Context, blockIdent ast.Ident) (dbInfo *perceptron.DBInfo, t causet.Block, err error) { 2493 is := d.GetSchemaReplicantWithInterceptor(ctx) 2494 schemaReplicant, ok := is.SchemaByName(blockIdent.Schema) 2495 if !ok { 2496 return nil, nil, schemareplicant.ErrDatabaseNotExists.GenWithStackByArgs(blockIdent.Schema) 2497 } 2498 t, err = is.TableByName(blockIdent.Schema, blockIdent.Name) 2499 if err != nil { 2500 return nil, nil, schemareplicant.ErrTableNotExists.GenWithStackByArgs(blockIdent.Schema, blockIdent.Name) 2501 } 2502 return schemaReplicant, t, nil 2503 } 2504 2505 func checkUnsupportedDeferredCausetConstraint(defCaus *ast.DeferredCausetDef, ti ast.Ident) error { 2506 for _, constraint := range defCaus.Options { 2507 switch constraint.Tp { 2508 case ast.DeferredCausetOptionAutoIncrement: 2509 return errUnsupportedAddDeferredCauset.GenWithStack("unsupported add defCausumn '%s' constraint AUTO_INCREMENT when altering '%s.%s'", defCaus.Name, ti.Schema, ti.Name) 2510 case ast.DeferredCausetOptionPrimaryKey: 2511 return errUnsupportedAddDeferredCauset.GenWithStack("unsupported add defCausumn '%s' constraint PRIMARY KEY when altering '%s.%s'", defCaus.Name, ti.Schema, ti.Name) 2512 case ast.DeferredCausetOptionUniqKey: 2513 return errUnsupportedAddDeferredCauset.GenWithStack("unsupported add defCausumn '%s' constraint UNIQUE KEY when altering '%s.%s'", defCaus.Name, ti.Schema, ti.Name) 2514 case ast.DeferredCausetOptionAutoRandom: 2515 errMsg := fmt.Sprintf(autoid.AutoRandomAlterAddDeferredCauset, defCaus.Name, ti.Schema, ti.Name) 2516 return ErrInvalidAutoRandom.GenWithStackByArgs(errMsg) 2517 } 2518 } 2519 2520 return nil 2521 } 2522 2523 func checkAndCreateNewDeferredCauset(ctx stochastikctx.Context, ti ast.Ident, schemaReplicant *perceptron.DBInfo, spec *ast.AlterTableSpec, t causet.Block, specNewDeferredCauset *ast.DeferredCausetDef) (*causet.DeferredCauset, error) { 2524 err := checkUnsupportedDeferredCausetConstraint(specNewDeferredCauset, ti) 2525 if err != nil { 2526 return nil, errors.Trace(err) 2527 } 2528 2529 defCausName := specNewDeferredCauset.Name.Name.O 2530 // Check whether added defCausumn has existed. 2531 defCaus := causet.FindDefCaus(t.DefCauss(), defCausName) 2532 if defCaus != nil { 2533 err = schemareplicant.ErrDeferredCausetExists.GenWithStackByArgs(defCausName) 2534 if spec.IfNotExists { 2535 ctx.GetStochastikVars().StmtCtx.AppendNote(err) 2536 return nil, nil 2537 } 2538 return nil, err 2539 } 2540 if err = checkDeferredCausetAttributes(defCausName, specNewDeferredCauset.Tp); err != nil { 2541 return nil, errors.Trace(err) 2542 } 2543 if len(defCausName) > allegrosql.MaxDeferredCausetNameLength { 2544 return nil, ErrTooLongIdent.GenWithStackByArgs(defCausName) 2545 } 2546 2547 // If new defCausumn is a generated defCausumn, do validation. 2548 // NOTE: we do check whether the defCausumn refers other generated 2549 // defCausumns occurring later in a causet, but we don't handle the defCaus offset. 2550 for _, option := range specNewDeferredCauset.Options { 2551 if option.Tp == ast.DeferredCausetOptionGenerated { 2552 if err := checkIllegalFn4GeneratedDeferredCauset(specNewDeferredCauset.Name.Name.L, option.Expr); err != nil { 2553 return nil, errors.Trace(err) 2554 } 2555 2556 if option.Stored { 2557 return nil, ErrUnsupportedOnGeneratedDeferredCauset.GenWithStackByArgs("Adding generated stored defCausumn through ALTER TABLE") 2558 } 2559 2560 _, dependDefCausNames := findDependedDeferredCausetNames(specNewDeferredCauset) 2561 if err = checkAutoIncrementRef(specNewDeferredCauset.Name.Name.L, dependDefCausNames, t.Meta()); err != nil { 2562 return nil, errors.Trace(err) 2563 } 2564 duplicateDefCausNames := make(map[string]struct{}, len(dependDefCausNames)) 2565 for k := range dependDefCausNames { 2566 duplicateDefCausNames[k] = struct{}{} 2567 } 2568 defcaus := t.DefCauss() 2569 2570 if err = checkDependedDefCausExist(dependDefCausNames, defcaus); err != nil { 2571 return nil, errors.Trace(err) 2572 } 2573 2574 if err = verifyDeferredCausetGenerationSingle(duplicateDefCausNames, defcaus, spec.Position); err != nil { 2575 return nil, errors.Trace(err) 2576 } 2577 } 2578 // Specially, since sequence has been supported, if a newly added defCausumn has a 2579 // sequence nextval function as it's default value option, it won't fill the 2580 // known rows with specific sequence next value under current add defCausumn logic. 2581 // More explanation can refer: TestSequenceDefaultLogic's comment in sequence_test.go 2582 if option.Tp == ast.DeferredCausetOptionDefaultValue { 2583 _, isSeqExpr, err := tryToGetSequenceDefaultValue(option) 2584 if err != nil { 2585 return nil, errors.Trace(err) 2586 } 2587 if isSeqExpr { 2588 return nil, errors.Trace(ErrAddDeferredCausetWithSequenceAsDefault.GenWithStackByArgs(specNewDeferredCauset.Name.Name.O)) 2589 } 2590 } 2591 } 2592 2593 blockCharset, blockDefCauslate, err := ResolveCharsetDefCauslation( 2594 ast.CharsetOpt{Chs: t.Meta().Charset, DefCaus: t.Meta().DefCauslate}, 2595 ast.CharsetOpt{Chs: schemaReplicant.Charset, DefCaus: schemaReplicant.DefCauslate}, 2596 ) 2597 if err != nil { 2598 return nil, errors.Trace(err) 2599 } 2600 // Ignore causet constraints now, they will be checked later. 2601 // We use length(t.DefCauss()) as the default offset firstly, we will change the defCausumn's offset later. 2602 defCaus, _, err = buildDeferredCausetAndConstraint( 2603 ctx, 2604 len(t.DefCauss()), 2605 specNewDeferredCauset, 2606 nil, 2607 blockCharset, 2608 blockDefCauslate, 2609 ) 2610 if err != nil { 2611 return nil, errors.Trace(err) 2612 } 2613 2614 defCaus.OriginDefaultValue, err = generateOriginDefaultValue(defCaus.ToInfo()) 2615 if err != nil { 2616 return nil, errors.Trace(err) 2617 } 2618 return defCaus, nil 2619 } 2620 2621 // AddDeferredCauset will add a new defCausumn to the causet. 2622 func (d *dbs) AddDeferredCauset(ctx stochastikctx.Context, ti ast.Ident, spec *ast.AlterTableSpec) error { 2623 specNewDeferredCauset := spec.NewDeferredCausets[0] 2624 schemaReplicant, t, err := d.getSchemaAndTableByIdent(ctx, ti) 2625 if err != nil { 2626 return errors.Trace(err) 2627 } 2628 if err = checkAddDeferredCausetTooManyDeferredCausets(len(t.DefCauss()) + 1); err != nil { 2629 return errors.Trace(err) 2630 } 2631 defCaus, err := checkAndCreateNewDeferredCauset(ctx, ti, schemaReplicant, spec, t, specNewDeferredCauset) 2632 if err != nil { 2633 return errors.Trace(err) 2634 } 2635 // Added defCausumn has existed and if_not_exists flag is true. 2636 if defCaus == nil { 2637 return nil 2638 } 2639 2640 job := &perceptron.Job{ 2641 SchemaID: schemaReplicant.ID, 2642 TableID: t.Meta().ID, 2643 SchemaName: schemaReplicant.Name.L, 2644 Type: perceptron.CausetActionAddDeferredCauset, 2645 BinlogInfo: &perceptron.HistoryInfo{}, 2646 Args: []interface{}{defCaus, spec.Position, 0}, 2647 } 2648 2649 err = d.doDBSJob(ctx, job) 2650 // defCausumn exists, but if_not_exists flags is true, so we ignore this error. 2651 if schemareplicant.ErrDeferredCausetExists.Equal(err) && spec.IfNotExists { 2652 ctx.GetStochastikVars().StmtCtx.AppendNote(err) 2653 return nil 2654 } 2655 err = d.callHookOnChanged(err) 2656 return errors.Trace(err) 2657 } 2658 2659 // AddDeferredCausets will add multi new defCausumns to the causet. 2660 func (d *dbs) AddDeferredCausets(ctx stochastikctx.Context, ti ast.Ident, specs []*ast.AlterTableSpec) error { 2661 schemaReplicant, t, err := d.getSchemaAndTableByIdent(ctx, ti) 2662 if err != nil { 2663 return errors.Trace(err) 2664 } 2665 2666 // Check all the defCausumns at once. 2667 addingDeferredCausetNames := make(map[string]bool) 2668 dupDeferredCausetNames := make(map[string]bool) 2669 for _, spec := range specs { 2670 for _, specNewDeferredCauset := range spec.NewDeferredCausets { 2671 if !addingDeferredCausetNames[specNewDeferredCauset.Name.Name.L] { 2672 addingDeferredCausetNames[specNewDeferredCauset.Name.Name.L] = true 2673 continue 2674 } 2675 if !spec.IfNotExists { 2676 return errors.Trace(schemareplicant.ErrDeferredCausetExists.GenWithStackByArgs(specNewDeferredCauset.Name.Name.O)) 2677 } 2678 dupDeferredCausetNames[specNewDeferredCauset.Name.Name.L] = true 2679 } 2680 } 2681 defCausumns := make([]*causet.DeferredCauset, 0, len(addingDeferredCausetNames)) 2682 positions := make([]*ast.DeferredCausetPosition, 0, len(addingDeferredCausetNames)) 2683 offsets := make([]int, 0, len(addingDeferredCausetNames)) 2684 ifNotExists := make([]bool, 0, len(addingDeferredCausetNames)) 2685 newDeferredCausetsCount := 0 2686 // Check the defCausumns one by one. 2687 for _, spec := range specs { 2688 for _, specNewDeferredCauset := range spec.NewDeferredCausets { 2689 if spec.IfNotExists && dupDeferredCausetNames[specNewDeferredCauset.Name.Name.L] { 2690 err = schemareplicant.ErrDeferredCausetExists.GenWithStackByArgs(specNewDeferredCauset.Name.Name.O) 2691 ctx.GetStochastikVars().StmtCtx.AppendNote(err) 2692 continue 2693 } 2694 defCaus, err := checkAndCreateNewDeferredCauset(ctx, ti, schemaReplicant, spec, t, specNewDeferredCauset) 2695 if err != nil { 2696 return errors.Trace(err) 2697 } 2698 // Added defCausumn has existed and if_not_exists flag is true. 2699 if defCaus == nil && spec.IfNotExists { 2700 continue 2701 } 2702 defCausumns = append(defCausumns, defCaus) 2703 positions = append(positions, spec.Position) 2704 offsets = append(offsets, 0) 2705 ifNotExists = append(ifNotExists, spec.IfNotExists) 2706 newDeferredCausetsCount++ 2707 } 2708 } 2709 if newDeferredCausetsCount == 0 { 2710 return nil 2711 } 2712 if err = checkAddDeferredCausetTooManyDeferredCausets(len(t.DefCauss()) + newDeferredCausetsCount); err != nil { 2713 return errors.Trace(err) 2714 } 2715 2716 job := &perceptron.Job{ 2717 SchemaID: schemaReplicant.ID, 2718 TableID: t.Meta().ID, 2719 SchemaName: schemaReplicant.Name.L, 2720 Type: perceptron.CausetActionAddDeferredCausets, 2721 BinlogInfo: &perceptron.HistoryInfo{}, 2722 Args: []interface{}{defCausumns, positions, offsets, ifNotExists}, 2723 } 2724 2725 err = d.doDBSJob(ctx, job) 2726 if err != nil { 2727 return errors.Trace(err) 2728 } 2729 err = d.callHookOnChanged(err) 2730 return errors.Trace(err) 2731 } 2732 2733 // AddTablePartitions will add a new partition to the causet. 2734 func (d *dbs) AddTablePartitions(ctx stochastikctx.Context, ident ast.Ident, spec *ast.AlterTableSpec) error { 2735 is := d.infoHandle.Get() 2736 schemaReplicant, ok := is.SchemaByName(ident.Schema) 2737 if !ok { 2738 return errors.Trace(schemareplicant.ErrDatabaseNotExists.GenWithStackByArgs(schemaReplicant)) 2739 } 2740 t, err := is.TableByName(ident.Schema, ident.Name) 2741 if err != nil { 2742 return errors.Trace(schemareplicant.ErrTableNotExists.GenWithStackByArgs(ident.Schema, ident.Name)) 2743 } 2744 2745 spacetime := t.Meta() 2746 pi := spacetime.GetPartitionInfo() 2747 if pi == nil { 2748 return errors.Trace(ErrPartitionMgmtOnNonpartitioned) 2749 } 2750 2751 partInfo, err := buildPartitionInfo(ctx, spacetime, d, spec) 2752 if err != nil { 2753 return errors.Trace(err) 2754 } 2755 2756 // partInfo contains only the new added partition, we have to combine it with the 2757 // old partitions to check all partitions is strictly increasing. 2758 tmp := *partInfo 2759 tmp.Definitions = append(pi.Definitions, tmp.Definitions...) 2760 spacetime.Partition = &tmp 2761 err = checkPartitionByRange(ctx, spacetime, nil) 2762 spacetime.Partition = pi 2763 if err != nil { 2764 if ErrSameNamePartition.Equal(err) && spec.IfNotExists { 2765 ctx.GetStochastikVars().StmtCtx.AppendNote(err) 2766 return nil 2767 } 2768 return errors.Trace(err) 2769 } 2770 2771 job := &perceptron.Job{ 2772 SchemaID: schemaReplicant.ID, 2773 TableID: spacetime.ID, 2774 SchemaName: schemaReplicant.Name.L, 2775 Type: perceptron.CausetActionAddTablePartition, 2776 BinlogInfo: &perceptron.HistoryInfo{}, 2777 Args: []interface{}{partInfo}, 2778 } 2779 2780 err = d.doDBSJob(ctx, job) 2781 if ErrSameNamePartition.Equal(err) && spec.IfNotExists { 2782 ctx.GetStochastikVars().StmtCtx.AppendNote(err) 2783 return nil 2784 } 2785 if err == nil { 2786 d.preSplitAndScatter(ctx, spacetime, partInfo) 2787 } 2788 err = d.callHookOnChanged(err) 2789 return errors.Trace(err) 2790 } 2791 2792 // CoalescePartitions coalesce partitions can be used with a causet that is partitioned by hash or key to reduce the number of partitions by number. 2793 func (d *dbs) CoalescePartitions(ctx stochastikctx.Context, ident ast.Ident, spec *ast.AlterTableSpec) error { 2794 is := d.infoHandle.Get() 2795 schemaReplicant, ok := is.SchemaByName(ident.Schema) 2796 if !ok { 2797 return errors.Trace(schemareplicant.ErrDatabaseNotExists.GenWithStackByArgs(schemaReplicant)) 2798 } 2799 t, err := is.TableByName(ident.Schema, ident.Name) 2800 if err != nil { 2801 return errors.Trace(schemareplicant.ErrTableNotExists.GenWithStackByArgs(ident.Schema, ident.Name)) 2802 } 2803 2804 spacetime := t.Meta() 2805 if spacetime.GetPartitionInfo() == nil { 2806 return errors.Trace(ErrPartitionMgmtOnNonpartitioned) 2807 } 2808 2809 switch spacetime.Partition.Type { 2810 // We don't support coalesce partitions hash type partition now. 2811 case perceptron.PartitionTypeHash: 2812 return errors.Trace(ErrUnsupportedCoalescePartition) 2813 2814 // Key type partition cannot be constructed currently, ignoring it for now. 2815 case perceptron.PartitionTypeKey: 2816 2817 // Coalesce partition can only be used on hash/key partitions. 2818 default: 2819 return errors.Trace(ErrCoalesceOnlyOnHashPartition) 2820 } 2821 2822 return errors.Trace(err) 2823 } 2824 2825 func (d *dbs) TruncateTablePartition(ctx stochastikctx.Context, ident ast.Ident, spec *ast.AlterTableSpec) error { 2826 is := d.infoHandle.Get() 2827 schemaReplicant, ok := is.SchemaByName(ident.Schema) 2828 if !ok { 2829 return errors.Trace(schemareplicant.ErrDatabaseNotExists.GenWithStackByArgs(schemaReplicant)) 2830 } 2831 t, err := is.TableByName(ident.Schema, ident.Name) 2832 if err != nil { 2833 return errors.Trace(schemareplicant.ErrTableNotExists.GenWithStackByArgs(ident.Schema, ident.Name)) 2834 } 2835 spacetime := t.Meta() 2836 if spacetime.GetPartitionInfo() == nil { 2837 return errors.Trace(ErrPartitionMgmtOnNonpartitioned) 2838 } 2839 2840 pids := make([]int64, len(spec.PartitionNames)) 2841 for i, name := range spec.PartitionNames { 2842 pid, err := blocks.FindPartitionByName(spacetime, name.L) 2843 if err != nil { 2844 return errors.Trace(err) 2845 } 2846 pids[i] = pid 2847 } 2848 2849 job := &perceptron.Job{ 2850 SchemaID: schemaReplicant.ID, 2851 TableID: spacetime.ID, 2852 SchemaName: schemaReplicant.Name.L, 2853 Type: perceptron.CausetActionTruncateTablePartition, 2854 BinlogInfo: &perceptron.HistoryInfo{}, 2855 Args: []interface{}{pids}, 2856 } 2857 2858 err = d.doDBSJob(ctx, job) 2859 if err != nil { 2860 return errors.Trace(err) 2861 } 2862 err = d.callHookOnChanged(err) 2863 return errors.Trace(err) 2864 } 2865 2866 func (d *dbs) DropTablePartition(ctx stochastikctx.Context, ident ast.Ident, spec *ast.AlterTableSpec) error { 2867 is := d.infoHandle.Get() 2868 schemaReplicant, ok := is.SchemaByName(ident.Schema) 2869 if !ok { 2870 return errors.Trace(schemareplicant.ErrDatabaseNotExists.GenWithStackByArgs(schemaReplicant)) 2871 } 2872 t, err := is.TableByName(ident.Schema, ident.Name) 2873 if err != nil { 2874 return errors.Trace(schemareplicant.ErrTableNotExists.GenWithStackByArgs(ident.Schema, ident.Name)) 2875 } 2876 spacetime := t.Meta() 2877 if spacetime.GetPartitionInfo() == nil { 2878 return errors.Trace(ErrPartitionMgmtOnNonpartitioned) 2879 } 2880 2881 partNames := make([]string, len(spec.PartitionNames)) 2882 for i, partCIName := range spec.PartitionNames { 2883 partNames[i] = partCIName.L 2884 } 2885 err = checkDropTablePartition(spacetime, partNames) 2886 if err != nil { 2887 if ErrDropPartitionNonExistent.Equal(err) && spec.IfExists { 2888 ctx.GetStochastikVars().StmtCtx.AppendNote(err) 2889 return nil 2890 } 2891 return errors.Trace(err) 2892 } 2893 2894 job := &perceptron.Job{ 2895 SchemaID: schemaReplicant.ID, 2896 TableID: spacetime.ID, 2897 SchemaName: schemaReplicant.Name.L, 2898 Type: perceptron.CausetActionDropTablePartition, 2899 BinlogInfo: &perceptron.HistoryInfo{}, 2900 Args: []interface{}{partNames}, 2901 } 2902 2903 err = d.doDBSJob(ctx, job) 2904 if err != nil { 2905 if ErrDropPartitionNonExistent.Equal(err) && spec.IfExists { 2906 ctx.GetStochastikVars().StmtCtx.AppendNote(err) 2907 return nil 2908 } 2909 return errors.Trace(err) 2910 } 2911 err = d.callHookOnChanged(err) 2912 return errors.Trace(err) 2913 } 2914 2915 func checkFieldTypeCompatible(ft *types.FieldType, other *types.FieldType) bool { 2916 // int(1) could match the type with int(8) 2917 partialEqual := ft.Tp == other.Tp && 2918 ft.Decimal == other.Decimal && 2919 ft.Charset == other.Charset && 2920 ft.DefCauslate == other.DefCauslate && 2921 (ft.Flen == other.Flen || ft.StorageLength() != types.VarStorageLen) && 2922 allegrosql.HasUnsignedFlag(ft.Flag) == allegrosql.HasUnsignedFlag(other.Flag) && 2923 allegrosql.HasAutoIncrementFlag(ft.Flag) == allegrosql.HasAutoIncrementFlag(other.Flag) && 2924 allegrosql.HasNotNullFlag(ft.Flag) == allegrosql.HasNotNullFlag(other.Flag) && 2925 allegrosql.HasZerofillFlag(ft.Flag) == allegrosql.HasZerofillFlag(other.Flag) && 2926 allegrosql.HasBinaryFlag(ft.Flag) == allegrosql.HasBinaryFlag(other.Flag) && 2927 allegrosql.HasPriKeyFlag(ft.Flag) == allegrosql.HasPriKeyFlag(other.Flag) 2928 if !partialEqual || len(ft.Elems) != len(other.Elems) { 2929 return false 2930 } 2931 for i := range ft.Elems { 2932 if ft.Elems[i] != other.Elems[i] { 2933 return false 2934 } 2935 } 2936 return true 2937 } 2938 2939 func checkTiFlashReplicaCompatible(source *perceptron.TiFlashReplicaInfo, target *perceptron.TiFlashReplicaInfo) bool { 2940 if source == target { 2941 return true 2942 } 2943 if source == nil || target == nil { 2944 return false 2945 } 2946 if source.Count != target.Count || 2947 source.Available != target.Available || len(source.LocationLabels) != len(target.LocationLabels) { 2948 return false 2949 } 2950 for i, lable := range source.LocationLabels { 2951 if target.LocationLabels[i] != lable { 2952 return false 2953 } 2954 } 2955 return true 2956 } 2957 2958 func checkTableDefCompatible(source *perceptron.TableInfo, target *perceptron.TableInfo) error { 2959 // check auto_random 2960 if source.AutoRandomBits != target.AutoRandomBits || 2961 source.Charset != target.Charset || 2962 source.DefCauslate != target.DefCauslate || 2963 source.ShardRowIDBits != target.ShardRowIDBits || 2964 source.MaxShardRowIDBits != target.MaxShardRowIDBits || 2965 !checkTiFlashReplicaCompatible(source.TiFlashReplica, target.TiFlashReplica) { 2966 return errors.Trace(ErrTablesDifferentMetadata) 2967 } 2968 if len(source.DefCauss()) != len(target.DefCauss()) { 2969 return errors.Trace(ErrTablesDifferentMetadata) 2970 } 2971 // DefCaus compatible check 2972 for i, sourceDefCaus := range source.DefCauss() { 2973 targetDefCaus := target.DefCauss()[i] 2974 if isVirtualGeneratedDeferredCauset(sourceDefCaus) != isVirtualGeneratedDeferredCauset(targetDefCaus) { 2975 return ErrUnsupportedOnGeneratedDeferredCauset.GenWithStackByArgs("Exchanging partitions for non-generated defCausumns") 2976 } 2977 // It should strictyle compare memexs for generated defCausumns 2978 if sourceDefCaus.Name.L != targetDefCaus.Name.L || 2979 sourceDefCaus.Hidden != targetDefCaus.Hidden || 2980 !checkFieldTypeCompatible(&sourceDefCaus.FieldType, &targetDefCaus.FieldType) || 2981 sourceDefCaus.GeneratedExprString != targetDefCaus.GeneratedExprString { 2982 return errors.Trace(ErrTablesDifferentMetadata) 2983 } 2984 if sourceDefCaus.State != perceptron.StatePublic || 2985 targetDefCaus.State != perceptron.StatePublic { 2986 return errors.Trace(ErrTablesDifferentMetadata) 2987 } 2988 if sourceDefCaus.ID != targetDefCaus.ID { 2989 return ErrPartitionExchangeDifferentOption.GenWithStackByArgs(fmt.Sprintf("defCausumn: %s", sourceDefCaus.Name)) 2990 } 2991 } 2992 if len(source.Indices) != len(target.Indices) { 2993 return errors.Trace(ErrTablesDifferentMetadata) 2994 } 2995 for _, sourceIdx := range source.Indices { 2996 var compatIdx *perceptron.IndexInfo 2997 for _, targetIdx := range target.Indices { 2998 if strings.EqualFold(sourceIdx.Name.L, targetIdx.Name.L) { 2999 compatIdx = targetIdx 3000 } 3001 } 3002 // No match index 3003 if compatIdx == nil { 3004 return errors.Trace(ErrTablesDifferentMetadata) 3005 } 3006 // Index type is not compatible 3007 if sourceIdx.Tp != compatIdx.Tp || 3008 sourceIdx.Unique != compatIdx.Unique || 3009 sourceIdx.Primary != compatIdx.Primary { 3010 return errors.Trace(ErrTablesDifferentMetadata) 3011 } 3012 // The index defCausumn 3013 if len(sourceIdx.DeferredCausets) != len(compatIdx.DeferredCausets) { 3014 return errors.Trace(ErrTablesDifferentMetadata) 3015 } 3016 for i, sourceIdxDefCaus := range sourceIdx.DeferredCausets { 3017 compatIdxDefCaus := compatIdx.DeferredCausets[i] 3018 if sourceIdxDefCaus.Length != compatIdxDefCaus.Length || 3019 sourceIdxDefCaus.Name.L != compatIdxDefCaus.Name.L { 3020 return errors.Trace(ErrTablesDifferentMetadata) 3021 } 3022 } 3023 if sourceIdx.ID != compatIdx.ID { 3024 return ErrPartitionExchangeDifferentOption.GenWithStackByArgs(fmt.Sprintf("index: %s", sourceIdx.Name)) 3025 } 3026 } 3027 3028 return nil 3029 } 3030 3031 func checkExchangePartition(pt *perceptron.TableInfo, nt *perceptron.TableInfo) error { 3032 if nt.IsView() || nt.IsSequence() { 3033 return errors.Trace(ErrCheckNoSuchTable) 3034 } 3035 if pt.GetPartitionInfo() == nil { 3036 return errors.Trace(ErrPartitionMgmtOnNonpartitioned) 3037 } 3038 if nt.GetPartitionInfo() != nil { 3039 return errors.Trace(ErrPartitionExchangePartTable.GenWithStackByArgs(nt.Name)) 3040 } 3041 3042 if nt.ForeignKeys != nil { 3043 return errors.Trace(ErrPartitionExchangeForeignKey.GenWithStackByArgs(nt.Name)) 3044 } 3045 3046 // NOTE: if nt is temporary causet, it should be checked 3047 return nil 3048 } 3049 3050 func (d *dbs) ExchangeTablePartition(ctx stochastikctx.Context, ident ast.Ident, spec *ast.AlterTableSpec) error { 3051 ptSchema, pt, err := d.getSchemaAndTableByIdent(ctx, ident) 3052 if err != nil { 3053 return errors.Trace(err) 3054 } 3055 3056 ptMeta := pt.Meta() 3057 3058 ntIdent := ast.Ident{Schema: spec.NewTable.Schema, Name: spec.NewTable.Name} 3059 ntSchema, nt, err := d.getSchemaAndTableByIdent(ctx, ntIdent) 3060 if err != nil { 3061 return errors.Trace(err) 3062 } 3063 3064 ntMeta := nt.Meta() 3065 3066 err = checkExchangePartition(ptMeta, ntMeta) 3067 if err != nil { 3068 return errors.Trace(err) 3069 } 3070 3071 partName := spec.PartitionNames[0].L 3072 3073 // NOTE: if pt is subPartitioned, it should be checked 3074 3075 defID, err := blocks.FindPartitionByName(ptMeta, partName) 3076 if err != nil { 3077 return errors.Trace(err) 3078 } 3079 3080 err = checkTableDefCompatible(ptMeta, ntMeta) 3081 if err != nil { 3082 return errors.Trace(err) 3083 } 3084 3085 job := &perceptron.Job{ 3086 SchemaID: ntSchema.ID, 3087 TableID: ntMeta.ID, 3088 SchemaName: ntSchema.Name.L, 3089 Type: perceptron.CausetActionExchangeTablePartition, 3090 BinlogInfo: &perceptron.HistoryInfo{}, 3091 Args: []interface{}{defID, ptSchema.ID, ptMeta.ID, partName, spec.WithValidation}, 3092 } 3093 3094 err = d.doDBSJob(ctx, job) 3095 if err != nil { 3096 return errors.Trace(err) 3097 } 3098 err = d.callHookOnChanged(err) 3099 return errors.Trace(err) 3100 } 3101 3102 // DropDeferredCauset will drop a defCausumn from the causet, now we don't support drop the defCausumn with index covered. 3103 func (d *dbs) DropDeferredCauset(ctx stochastikctx.Context, ti ast.Ident, spec *ast.AlterTableSpec) error { 3104 schemaReplicant, t, err := d.getSchemaAndTableByIdent(ctx, ti) 3105 if err != nil { 3106 return errors.Trace(err) 3107 } 3108 3109 isDropable, err := checkIsDroppableDeferredCauset(ctx, t, spec) 3110 if err != nil { 3111 return err 3112 } 3113 if !isDropable { 3114 return nil 3115 } 3116 defCausName := spec.OldDeferredCausetName.Name 3117 3118 job := &perceptron.Job{ 3119 SchemaID: schemaReplicant.ID, 3120 TableID: t.Meta().ID, 3121 SchemaName: schemaReplicant.Name.L, 3122 Type: perceptron.CausetActionDropDeferredCauset, 3123 BinlogInfo: &perceptron.HistoryInfo{}, 3124 Args: []interface{}{defCausName}, 3125 } 3126 3127 err = d.doDBSJob(ctx, job) 3128 // defCausumn not exists, but if_exists flags is true, so we ignore this error. 3129 if ErrCantDropFieldOrKey.Equal(err) && spec.IfExists { 3130 ctx.GetStochastikVars().StmtCtx.AppendNote(err) 3131 return nil 3132 } 3133 err = d.callHookOnChanged(err) 3134 return errors.Trace(err) 3135 } 3136 3137 // DropDeferredCausets will drop multi-defCausumns from the causet, now we don't support drop the defCausumn with index covered. 3138 func (d *dbs) DropDeferredCausets(ctx stochastikctx.Context, ti ast.Ident, specs []*ast.AlterTableSpec) error { 3139 schemaReplicant, t, err := d.getSchemaAndTableByIdent(ctx, ti) 3140 if err != nil { 3141 return errors.Trace(err) 3142 } 3143 tblInfo := t.Meta() 3144 3145 dropingDeferredCausetNames := make(map[string]bool) 3146 dupDeferredCausetNames := make(map[string]bool) 3147 for _, spec := range specs { 3148 if !dropingDeferredCausetNames[spec.OldDeferredCausetName.Name.L] { 3149 dropingDeferredCausetNames[spec.OldDeferredCausetName.Name.L] = true 3150 } else { 3151 if spec.IfExists { 3152 dupDeferredCausetNames[spec.OldDeferredCausetName.Name.L] = true 3153 continue 3154 } 3155 return errors.Trace(ErrCantDropFieldOrKey.GenWithStack("defCausumn %s doesn't exist", spec.OldDeferredCausetName.Name.O)) 3156 } 3157 } 3158 3159 ifExists := make([]bool, 0, len(specs)) 3160 defCausNames := make([]perceptron.CIStr, 0, len(specs)) 3161 for _, spec := range specs { 3162 if spec.IfExists && dupDeferredCausetNames[spec.OldDeferredCausetName.Name.L] { 3163 err = ErrCantDropFieldOrKey.GenWithStack("defCausumn %s doesn't exist", spec.OldDeferredCausetName.Name.L) 3164 ctx.GetStochastikVars().StmtCtx.AppendNote(err) 3165 continue 3166 } 3167 isDropable, err := checkIsDroppableDeferredCauset(ctx, t, spec) 3168 if err != nil { 3169 return err 3170 } 3171 // DeferredCauset can't drop and if_exists flag is true. 3172 if !isDropable && spec.IfExists { 3173 continue 3174 } 3175 defCausNames = append(defCausNames, spec.OldDeferredCausetName.Name) 3176 ifExists = append(ifExists, spec.IfExists) 3177 } 3178 if len(defCausNames) == 0 { 3179 return nil 3180 } 3181 if len(tblInfo.DeferredCausets) == len(defCausNames) { 3182 return ErrCantRemoveAllFields.GenWithStack("can't drop all defCausumns in causet %s", 3183 tblInfo.Name) 3184 } 3185 3186 job := &perceptron.Job{ 3187 SchemaID: schemaReplicant.ID, 3188 TableID: t.Meta().ID, 3189 SchemaName: schemaReplicant.Name.L, 3190 Type: perceptron.CausetActionDropDeferredCausets, 3191 BinlogInfo: &perceptron.HistoryInfo{}, 3192 Args: []interface{}{defCausNames, ifExists}, 3193 } 3194 3195 err = d.doDBSJob(ctx, job) 3196 if err != nil { 3197 return errors.Trace(err) 3198 } 3199 err = d.callHookOnChanged(err) 3200 return errors.Trace(err) 3201 } 3202 3203 func checkIsDroppableDeferredCauset(ctx stochastikctx.Context, t causet.Block, spec *ast.AlterTableSpec) (isDrapable bool, err error) { 3204 tblInfo := t.Meta() 3205 // Check whether dropped defCausumn has existed. 3206 defCausName := spec.OldDeferredCausetName.Name 3207 defCaus := causet.FindDefCaus(t.VisibleDefCauss(), defCausName.L) 3208 if defCaus == nil { 3209 err = ErrCantDropFieldOrKey.GenWithStack("defCausumn %s doesn't exist", defCausName) 3210 if spec.IfExists { 3211 ctx.GetStochastikVars().StmtCtx.AppendNote(err) 3212 return false, nil 3213 } 3214 return false, err 3215 } 3216 3217 if err = isDroppableDeferredCauset(tblInfo, defCausName); err != nil { 3218 return false, errors.Trace(err) 3219 } 3220 // We don't support dropping defCausumn with PK handle covered now. 3221 if defCaus.IsPKHandleDeferredCauset(tblInfo) { 3222 return false, errUnsupportedPKHandle 3223 } 3224 return true, nil 3225 } 3226 3227 // checkModifyCharsetAndDefCauslation returns error when the charset or defCauslation is not modifiable. 3228 // needRewriteDefCauslationData is used when trying to modify the defCauslation of a defCausumn, it is true when the defCausumn is with 3229 // index because index of a string defCausumn is defCauslation-aware. 3230 func checkModifyCharsetAndDefCauslation(toCharset, toDefCauslate, origCharset, origDefCauslate string, needRewriteDefCauslationData bool) error { 3231 if !charset.ValidCharsetAndDefCauslation(toCharset, toDefCauslate) { 3232 return ErrUnknownCharacterSet.GenWithStack("Unknown character set: '%s', defCauslation: '%s'", toCharset, toDefCauslate) 3233 } 3234 3235 if needRewriteDefCauslationData && defCauslate.NewDefCauslationEnabled() && !defCauslate.CompatibleDefCauslate(origDefCauslate, toDefCauslate) { 3236 return errUnsupportedModifyDefCauslation.GenWithStackByArgs(origDefCauslate, toDefCauslate) 3237 } 3238 3239 if (origCharset == charset.CharsetUTF8 && toCharset == charset.CharsetUTF8MB4) || 3240 (origCharset == charset.CharsetUTF8 && toCharset == charset.CharsetUTF8) || 3241 (origCharset == charset.CharsetUTF8MB4 && toCharset == charset.CharsetUTF8MB4) { 3242 // MilevaDB only allow utf8 to be changed to utf8mb4, or changing the defCauslation when the charset is utf8/utf8mb4. 3243 return nil 3244 } 3245 3246 if toCharset != origCharset { 3247 msg := fmt.Sprintf("charset from %s to %s", origCharset, toCharset) 3248 return errUnsupportedModifyCharset.GenWithStackByArgs(msg) 3249 } 3250 if toDefCauslate != origDefCauslate { 3251 msg := fmt.Sprintf("change defCauslate from %s to %s", origDefCauslate, toDefCauslate) 3252 return errUnsupportedModifyCharset.GenWithStackByArgs(msg) 3253 } 3254 return nil 3255 } 3256 3257 // CheckModifyTypeCompatible checks whether changes defCausumn type to another is compatible considering 3258 // field length and precision. 3259 func CheckModifyTypeCompatible(origin *types.FieldType, to *types.FieldType) (allowedChangeDeferredCausetValueMsg string, err error) { 3260 unsupportedMsg := fmt.Sprintf("type %v not match origin %v", to.CompactStr(), origin.CompactStr()) 3261 var isIntType bool 3262 switch origin.Tp { 3263 case allegrosql.TypeVarchar, allegrosql.TypeString, allegrosql.TypeVarString, 3264 allegrosql.TypeBlob, allegrosql.TypeTinyBlob, allegrosql.TypeMediumBlob, allegrosql.TypeLongBlob: 3265 switch to.Tp { 3266 case allegrosql.TypeVarchar, allegrosql.TypeString, allegrosql.TypeVarString, 3267 allegrosql.TypeBlob, allegrosql.TypeTinyBlob, allegrosql.TypeMediumBlob, allegrosql.TypeLongBlob: 3268 default: 3269 return "", errUnsupportedModifyDeferredCauset.GenWithStackByArgs(unsupportedMsg) 3270 } 3271 case allegrosql.TypeTiny, allegrosql.TypeShort, allegrosql.TypeInt24, allegrosql.TypeLong, allegrosql.TypeLonglong: 3272 switch to.Tp { 3273 case allegrosql.TypeTiny, allegrosql.TypeShort, allegrosql.TypeInt24, allegrosql.TypeLong, allegrosql.TypeLonglong: 3274 isIntType = true 3275 default: 3276 return "", errUnsupportedModifyDeferredCauset.GenWithStackByArgs(unsupportedMsg) 3277 } 3278 case allegrosql.TypeEnum, allegrosql.TypeSet: 3279 var typeVar string 3280 if origin.Tp == allegrosql.TypeEnum { 3281 typeVar = "enum" 3282 } else { 3283 typeVar = "set" 3284 } 3285 if origin.Tp != to.Tp { 3286 msg := fmt.Sprintf("cannot modify %s type defCausumn's to type %s", typeVar, to.String()) 3287 return "", errUnsupportedModifyDeferredCauset.GenWithStackByArgs(msg) 3288 } 3289 if len(to.Elems) < len(origin.Elems) { 3290 msg := fmt.Sprintf("the number of %s defCausumn's elements is less than the original: %d", typeVar, len(origin.Elems)) 3291 return "", errUnsupportedModifyDeferredCauset.GenWithStackByArgs(msg) 3292 } 3293 for index, originElem := range origin.Elems { 3294 toElem := to.Elems[index] 3295 if originElem != toElem { 3296 msg := fmt.Sprintf("cannot modify %s defCausumn value %s to %s", typeVar, originElem, toElem) 3297 return "", errUnsupportedModifyDeferredCauset.GenWithStackByArgs(msg) 3298 } 3299 } 3300 case allegrosql.TypeNewDecimal: 3301 if origin.Tp != to.Tp { 3302 return "", errUnsupportedModifyDeferredCauset.GenWithStackByArgs(unsupportedMsg) 3303 } 3304 // The root cause is modifying decimal precision needs to rewrite binary representation of that decimal. 3305 if to.Flen != origin.Flen || to.Decimal != origin.Decimal { 3306 return "", errUnsupportedModifyDeferredCauset.GenWithStackByArgs("can't change decimal defCausumn precision") 3307 } 3308 default: 3309 if origin.Tp != to.Tp { 3310 return "", errUnsupportedModifyDeferredCauset.GenWithStackByArgs(unsupportedMsg) 3311 } 3312 } 3313 3314 if to.Flen > 0 && to.Flen < origin.Flen { 3315 msg := fmt.Sprintf("length %d is less than origin %d", to.Flen, origin.Flen) 3316 if isIntType { 3317 return msg, errUnsupportedModifyDeferredCauset.GenWithStackByArgs(msg) 3318 } 3319 return "", errUnsupportedModifyDeferredCauset.GenWithStackByArgs(msg) 3320 } 3321 if to.Decimal > 0 && to.Decimal < origin.Decimal { 3322 msg := fmt.Sprintf("decimal %d is less than origin %d", to.Decimal, origin.Decimal) 3323 return "", errUnsupportedModifyDeferredCauset.GenWithStackByArgs(msg) 3324 } 3325 3326 toUnsigned := allegrosql.HasUnsignedFlag(to.Flag) 3327 originUnsigned := allegrosql.HasUnsignedFlag(origin.Flag) 3328 if originUnsigned != toUnsigned { 3329 msg := fmt.Sprintf("can't change unsigned integer to signed or vice versa") 3330 if isIntType { 3331 return msg, errUnsupportedModifyDeferredCauset.GenWithStackByArgs(msg) 3332 } 3333 return "", errUnsupportedModifyDeferredCauset.GenWithStackByArgs(msg) 3334 } 3335 return "", nil 3336 } 3337 3338 // checkModifyTypes checks if the 'origin' type can be modified to 'to' type with out the need to 3339 // change or check existing data in the causet. 3340 // It returns error if the two types has incompatible Charset and DefCauslation, different sign, different 3341 // digital/string types, or length of new Flen and Decimal is less than origin. 3342 func checkModifyTypes(ctx stochastikctx.Context, origin *types.FieldType, to *types.FieldType, needRewriteDefCauslationData bool) error { 3343 changeDeferredCausetValueMsg, err := CheckModifyTypeCompatible(origin, to) 3344 if err != nil { 3345 enableChangeDeferredCausetType := ctx.GetStochastikVars().EnableChangeDeferredCausetType 3346 if len(changeDeferredCausetValueMsg) == 0 { 3347 return errors.Trace(err) 3348 } 3349 3350 if !enableChangeDeferredCausetType { 3351 msg := fmt.Sprintf("%s, and milevadb_enable_change_defCausumn_type is false", changeDeferredCausetValueMsg) 3352 return errUnsupportedModifyDeferredCauset.GenWithStackByArgs(msg) 3353 } else if allegrosql.HasPriKeyFlag(origin.Flag) { 3354 msg := "milevadb_enable_change_defCausumn_type is true and this defCausumn has primary key flag" 3355 return errUnsupportedModifyDeferredCauset.GenWithStackByArgs(msg) 3356 } 3357 } 3358 3359 err = checkModifyCharsetAndDefCauslation(to.Charset, to.DefCauslate, origin.Charset, origin.DefCauslate, needRewriteDefCauslationData) 3360 return errors.Trace(err) 3361 } 3362 3363 func setDefaultValue(ctx stochastikctx.Context, defCaus *causet.DeferredCauset, option *ast.DeferredCausetOption) (bool, error) { 3364 hasDefaultValue := false 3365 value, isSeqExpr, err := getDefaultValue(ctx, defCaus, option) 3366 if err != nil { 3367 return hasDefaultValue, errors.Trace(err) 3368 } 3369 if isSeqExpr { 3370 if err := checkSequenceDefaultValue(defCaus); err != nil { 3371 return false, errors.Trace(err) 3372 } 3373 defCaus.DefaultIsExpr = isSeqExpr 3374 } 3375 3376 if hasDefaultValue, value, err = checkDeferredCausetDefaultValue(ctx, defCaus, value); err != nil { 3377 return hasDefaultValue, errors.Trace(err) 3378 } 3379 value, err = convertTimestamFIDelefaultValToUTC(ctx, value, defCaus) 3380 if err != nil { 3381 return hasDefaultValue, errors.Trace(err) 3382 } 3383 err = defCaus.SetDefaultValue(value) 3384 if err != nil { 3385 return hasDefaultValue, errors.Trace(err) 3386 } 3387 return hasDefaultValue, nil 3388 } 3389 3390 func setDeferredCausetComment(ctx stochastikctx.Context, defCaus *causet.DeferredCauset, option *ast.DeferredCausetOption) error { 3391 value, err := memex.EvalAstExpr(ctx, option.Expr) 3392 if err != nil { 3393 return errors.Trace(err) 3394 } 3395 defCaus.Comment, err = value.ToString() 3396 return errors.Trace(err) 3397 } 3398 3399 // processDeferredCausetOptions is only used in getModifiableDeferredCausetJob. 3400 func processDeferredCausetOptions(ctx stochastikctx.Context, defCaus *causet.DeferredCauset, options []*ast.DeferredCausetOption) error { 3401 var sb strings.Builder 3402 restoreFlags := format.RestoreStringSingleQuotes | format.RestoreKeyWordLowercase | format.RestoreNameBackQuotes | 3403 format.RestoreSpacesAroundBinaryOperation 3404 restoreCtx := format.NewRestoreCtx(restoreFlags, &sb) 3405 3406 var hasDefaultValue, setOnUFIDelateNow bool 3407 var err error 3408 for _, opt := range options { 3409 switch opt.Tp { 3410 case ast.DeferredCausetOptionDefaultValue: 3411 hasDefaultValue, err = setDefaultValue(ctx, defCaus, opt) 3412 if err != nil { 3413 return errors.Trace(err) 3414 } 3415 case ast.DeferredCausetOptionComment: 3416 err := setDeferredCausetComment(ctx, defCaus, opt) 3417 if err != nil { 3418 return errors.Trace(err) 3419 } 3420 case ast.DeferredCausetOptionNotNull: 3421 defCaus.Flag |= allegrosql.NotNullFlag 3422 case ast.DeferredCausetOptionNull: 3423 defCaus.Flag &= ^allegrosql.NotNullFlag 3424 case ast.DeferredCausetOptionAutoIncrement: 3425 defCaus.Flag |= allegrosql.AutoIncrementFlag 3426 case ast.DeferredCausetOptionPrimaryKey, ast.DeferredCausetOptionUniqKey: 3427 return errUnsupportedModifyDeferredCauset.GenWithStack("can't change defCausumn constraint - %v", opt.Tp) 3428 case ast.DeferredCausetOptionOnUFIDelate: 3429 // TODO: Support other time functions. 3430 if defCaus.Tp == allegrosql.TypeTimestamp || defCaus.Tp == allegrosql.TypeDatetime { 3431 if !memex.IsValidCurrentTimestampExpr(opt.Expr, &defCaus.FieldType) { 3432 return ErrInvalidOnUFIDelate.GenWithStackByArgs(defCaus.Name) 3433 } 3434 } else { 3435 return ErrInvalidOnUFIDelate.GenWithStackByArgs(defCaus.Name) 3436 } 3437 defCaus.Flag |= allegrosql.OnUFIDelateNowFlag 3438 setOnUFIDelateNow = true 3439 case ast.DeferredCausetOptionGenerated: 3440 sb.Reset() 3441 err = opt.Expr.Restore(restoreCtx) 3442 if err != nil { 3443 return errors.Trace(err) 3444 } 3445 defCaus.GeneratedExprString = sb.String() 3446 defCaus.GeneratedStored = opt.Stored 3447 defCaus.Dependences = make(map[string]struct{}) 3448 defCaus.GeneratedExpr = opt.Expr 3449 for _, defCausName := range findDeferredCausetNamesInExpr(opt.Expr) { 3450 defCaus.Dependences[defCausName.Name.L] = struct{}{} 3451 } 3452 case ast.DeferredCausetOptionDefCauslate: 3453 defCaus.DefCauslate = opt.StrValue 3454 case ast.DeferredCausetOptionReference: 3455 return errors.Trace(errUnsupportedModifyDeferredCauset.GenWithStackByArgs("can't modify with references")) 3456 case ast.DeferredCausetOptionFulltext: 3457 return errors.Trace(errUnsupportedModifyDeferredCauset.GenWithStackByArgs("can't modify with full text")) 3458 case ast.DeferredCausetOptionCheck: 3459 return errors.Trace(errUnsupportedModifyDeferredCauset.GenWithStackByArgs("can't modify with check")) 3460 // Ignore DeferredCausetOptionAutoRandom. It will be handled later. 3461 case ast.DeferredCausetOptionAutoRandom: 3462 default: 3463 return errors.Trace(errUnsupportedModifyDeferredCauset.GenWithStackByArgs(fmt.Sprintf("unknown defCausumn option type: %d", opt.Tp))) 3464 } 3465 } 3466 3467 processDefaultValue(defCaus, hasDefaultValue, setOnUFIDelateNow) 3468 3469 processDeferredCausetFlags(defCaus) 3470 3471 if hasDefaultValue { 3472 return errors.Trace(checkDefaultValue(ctx, defCaus, true)) 3473 } 3474 3475 return nil 3476 } 3477 3478 func (d *dbs) getModifiableDeferredCausetJob(ctx stochastikctx.Context, ident ast.Ident, originalDefCausName perceptron.CIStr, 3479 spec *ast.AlterTableSpec) (*perceptron.Job, error) { 3480 specNewDeferredCauset := spec.NewDeferredCausets[0] 3481 is := d.infoHandle.Get() 3482 schemaReplicant, ok := is.SchemaByName(ident.Schema) 3483 if !ok { 3484 return nil, errors.Trace(schemareplicant.ErrDatabaseNotExists) 3485 } 3486 t, err := is.TableByName(ident.Schema, ident.Name) 3487 if err != nil { 3488 return nil, errors.Trace(schemareplicant.ErrTableNotExists.GenWithStackByArgs(ident.Schema, ident.Name)) 3489 } 3490 3491 defCaus := causet.FindDefCaus(t.DefCauss(), originalDefCausName.L) 3492 if defCaus == nil { 3493 return nil, schemareplicant.ErrDeferredCausetNotExists.GenWithStackByArgs(originalDefCausName, ident.Name) 3494 } 3495 newDefCausName := specNewDeferredCauset.Name.Name 3496 if newDefCausName.L == perceptron.ExtraHandleName.L { 3497 return nil, ErrWrongDeferredCausetName.GenWithStackByArgs(newDefCausName.L) 3498 } 3499 // If we want to rename the defCausumn name, we need to check whether it already exists. 3500 if newDefCausName.L != originalDefCausName.L { 3501 c := causet.FindDefCaus(t.DefCauss(), newDefCausName.L) 3502 if c != nil { 3503 return nil, schemareplicant.ErrDeferredCausetExists.GenWithStackByArgs(newDefCausName) 3504 } 3505 } 3506 // Check the defCausumn with foreign key. 3507 if fkInfo := getDeferredCausetForeignKeyInfo(originalDefCausName.L, t.Meta().ForeignKeys); fkInfo != nil { 3508 return nil, errFKIncompatibleDeferredCausets.GenWithStackByArgs(originalDefCausName, fkInfo.Name) 3509 } 3510 3511 // Constraints in the new defCausumn means adding new constraints. Errors should thrown, 3512 // which will be done by `processDeferredCausetOptions` later. 3513 if specNewDeferredCauset.Tp == nil { 3514 // Make sure the defCausumn definition is simple field type. 3515 return nil, errors.Trace(errUnsupportedModifyDeferredCauset) 3516 } 3517 3518 if err = checkDeferredCausetAttributes(specNewDeferredCauset.Name.OrigDefCausName(), specNewDeferredCauset.Tp); err != nil { 3519 return nil, errors.Trace(err) 3520 } 3521 3522 newDefCaus := causet.ToDeferredCauset(&perceptron.DeferredCausetInfo{ 3523 ID: defCaus.ID, 3524 // We use this PR(https://github.com/whtcorpsinc/milevadb/pull/6274) as the dividing line to define whether it is a new version or an old version MilevaDB. 3525 // The old version MilevaDB initializes the defCausumn's offset and state here. 3526 // The new version MilevaDB doesn't initialize the defCausumn's offset and state, and it will do the initialization in run DBS function. 3527 // When we do the rolling upgrade the following may happen: 3528 // a new version MilevaDB builds the DBS job that doesn't be set the defCausumn's offset and state, 3529 // and the old version MilevaDB is the DBS tenant, it doesn't get offset and state from the causetstore. Then it will encounter errors. 3530 // So here we set offset and state to support the rolling upgrade. 3531 Offset: defCaus.Offset, 3532 State: defCaus.State, 3533 OriginDefaultValue: defCaus.OriginDefaultValue, 3534 FieldType: *specNewDeferredCauset.Tp, 3535 Name: newDefCausName, 3536 Version: defCaus.Version, 3537 }) 3538 3539 var chs, defCausl string 3540 // TODO: Remove it when all causet versions are greater than or equal to TableInfoVersion1. 3541 // If newDefCaus's charset is empty and the causet's version less than TableInfoVersion1, 3542 // we will not modify the charset of the defCausumn. This behavior is not compatible with MyALLEGROSQL. 3543 if len(newDefCaus.FieldType.Charset) == 0 && t.Meta().Version < perceptron.TableInfoVersion1 { 3544 chs = defCaus.FieldType.Charset 3545 defCausl = defCaus.FieldType.DefCauslate 3546 } else { 3547 chs, defCausl, err = getCharsetAndDefCauslateInDeferredCausetDef(specNewDeferredCauset) 3548 if err != nil { 3549 return nil, errors.Trace(err) 3550 } 3551 chs, defCausl, err = ResolveCharsetDefCauslation( 3552 ast.CharsetOpt{Chs: chs, DefCaus: defCausl}, 3553 ast.CharsetOpt{Chs: t.Meta().Charset, DefCaus: t.Meta().DefCauslate}, 3554 ast.CharsetOpt{Chs: schemaReplicant.Charset, DefCaus: schemaReplicant.DefCauslate}, 3555 ) 3556 if err != nil { 3557 return nil, errors.Trace(err) 3558 } 3559 } 3560 3561 if err = setCharsetDefCauslationFlenDecimal(&newDefCaus.FieldType, chs, defCausl); err != nil { 3562 return nil, errors.Trace(err) 3563 } 3564 3565 if err = processDeferredCausetOptions(ctx, newDefCaus, specNewDeferredCauset.Options); err != nil { 3566 return nil, errors.Trace(err) 3567 } 3568 3569 if err = checkDeferredCausetValueConstraint(newDefCaus, newDefCaus.DefCauslate); err != nil { 3570 return nil, errors.Trace(err) 3571 } 3572 3573 if err = checkModifyTypes(ctx, &defCaus.FieldType, &newDefCaus.FieldType, isDeferredCausetWithIndex(defCaus.Name.L, t.Meta().Indices)); err != nil { 3574 if strings.Contains(err.Error(), "Unsupported modifying defCauslation") { 3575 defCausErrMsg := "Unsupported modifying defCauslation of defCausumn '%s' from '%s' to '%s' when index is defined on it." 3576 err = errUnsupportedModifyDefCauslation.GenWithStack(defCausErrMsg, defCaus.Name.L, defCaus.DefCauslate, newDefCaus.DefCauslate) 3577 } 3578 return nil, errors.Trace(err) 3579 } 3580 if ctx.GetStochastikVars().EnableChangeDeferredCausetType && needChangeDeferredCausetData(defCaus.DeferredCausetInfo, newDefCaus.DeferredCausetInfo) { 3581 if newDefCaus.IsGenerated() || defCaus.IsGenerated() { 3582 // TODO: Make it compatible with MyALLEGROSQL error. 3583 msg := fmt.Sprintf("milevadb_enable_change_defCausumn_type is true, newDefCaus IsGenerated %v, oldDefCaus IsGenerated %v", newDefCaus.IsGenerated(), defCaus.IsGenerated()) 3584 return nil, errUnsupportedModifyDeferredCauset.GenWithStackByArgs(msg) 3585 } else if t.Meta().Partition != nil { 3586 return nil, errUnsupportedModifyDeferredCauset.GenWithStackByArgs("milevadb_enable_change_defCausumn_type is true, causet is partition causet") 3587 } 3588 } 3589 3590 // Copy index related options to the new spec. 3591 indexFlags := defCaus.FieldType.Flag & (allegrosql.PriKeyFlag | allegrosql.UniqueKeyFlag | allegrosql.MultipleKeyFlag) 3592 newDefCaus.FieldType.Flag |= indexFlags 3593 if allegrosql.HasPriKeyFlag(defCaus.FieldType.Flag) { 3594 newDefCaus.FieldType.Flag |= allegrosql.NotNullFlag 3595 // TODO: If user explicitly set NULL, we should throw error ErrPrimaryCantHaveNull. 3596 } 3597 3598 // We don't support modifying defCausumn from not_auto_increment to auto_increment. 3599 if !allegrosql.HasAutoIncrementFlag(defCaus.Flag) && allegrosql.HasAutoIncrementFlag(newDefCaus.Flag) { 3600 return nil, errUnsupportedModifyDeferredCauset.GenWithStackByArgs("can't set auto_increment") 3601 } 3602 // Disallow modifying defCausumn from auto_increment to not auto_increment if the stochastik variable `AllowRemoveAutoInc` is false. 3603 if !ctx.GetStochastikVars().AllowRemoveAutoInc && allegrosql.HasAutoIncrementFlag(defCaus.Flag) && !allegrosql.HasAutoIncrementFlag(newDefCaus.Flag) { 3604 return nil, errUnsupportedModifyDeferredCauset.GenWithStackByArgs("can't remove auto_increment without @@milevadb_allow_remove_auto_inc enabled") 3605 } 3606 3607 // We support modifying the type definitions of 'null' to 'not null' now. 3608 var modifyDeferredCausetTp byte 3609 if !allegrosql.HasNotNullFlag(defCaus.Flag) && allegrosql.HasNotNullFlag(newDefCaus.Flag) { 3610 if err = checkForNullValue(ctx, defCaus.Tp != newDefCaus.Tp, ident.Schema, ident.Name, newDefCaus.Name, defCaus.DeferredCausetInfo); err != nil { 3611 return nil, errors.Trace(err) 3612 } 3613 // `modifyDeferredCausetTp` indicates that there is a type modification. 3614 modifyDeferredCausetTp = allegrosql.TypeNull 3615 } 3616 3617 if err = checkDeferredCausetFieldLength(newDefCaus); err != nil { 3618 return nil, err 3619 } 3620 3621 if err = checkDeferredCausetWithIndexConstraint(t.Meta(), defCaus.DeferredCausetInfo, newDefCaus.DeferredCausetInfo); err != nil { 3622 return nil, err 3623 } 3624 3625 // As same with MyALLEGROSQL, we don't support modifying the stored status for generated defCausumns. 3626 if err = checkModifyGeneratedDeferredCauset(t, defCaus, newDefCaus, specNewDeferredCauset); err != nil { 3627 return nil, errors.Trace(err) 3628 } 3629 3630 var newAutoRandBits uint64 3631 if newAutoRandBits, err = checkAutoRandom(t.Meta(), defCaus, specNewDeferredCauset); err != nil { 3632 return nil, errors.Trace(err) 3633 } 3634 3635 job := &perceptron.Job{ 3636 SchemaID: schemaReplicant.ID, 3637 TableID: t.Meta().ID, 3638 SchemaName: schemaReplicant.Name.L, 3639 Type: perceptron.CausetActionModifyDeferredCauset, 3640 BinlogInfo: &perceptron.HistoryInfo{}, 3641 Args: []interface{}{&newDefCaus, originalDefCausName, spec.Position, modifyDeferredCausetTp, newAutoRandBits}, 3642 } 3643 return job, nil 3644 } 3645 3646 // checkDeferredCausetWithIndexConstraint is used to check the related index constraint of the modified defCausumn. 3647 // Index has a max-prefix-length constraint. eg: a varchar(100), index idx(a), modifying defCausumn a to a varchar(4000) 3648 // will cause index idx to break the max-prefix-length constraint. 3649 func checkDeferredCausetWithIndexConstraint(tbInfo *perceptron.TableInfo, originalDefCaus, newDefCaus *perceptron.DeferredCausetInfo) error { 3650 var defCausumns []*perceptron.DeferredCausetInfo 3651 for _, indexInfo := range tbInfo.Indices { 3652 containDeferredCauset := false 3653 for _, defCaus := range indexInfo.DeferredCausets { 3654 if defCaus.Name.L == originalDefCaus.Name.L { 3655 containDeferredCauset = true 3656 break 3657 } 3658 } 3659 if !containDeferredCauset { 3660 continue 3661 } 3662 if defCausumns == nil { 3663 defCausumns = make([]*perceptron.DeferredCausetInfo, 0, len(tbInfo.DeferredCausets)) 3664 defCausumns = append(defCausumns, tbInfo.DeferredCausets...) 3665 // replace old defCausumn with new defCausumn. 3666 for i, defCaus := range defCausumns { 3667 if defCaus.Name.L != originalDefCaus.Name.L { 3668 continue 3669 } 3670 defCausumns[i] = newDefCaus.Clone() 3671 defCausumns[i].Name = originalDefCaus.Name 3672 break 3673 } 3674 } 3675 err := checHoTTexPrefixLength(defCausumns, indexInfo.DeferredCausets) 3676 if err != nil { 3677 return err 3678 } 3679 } 3680 return nil 3681 } 3682 3683 func checkAutoRandom(blockInfo *perceptron.TableInfo, originDefCaus *causet.DeferredCauset, specNewDeferredCauset *ast.DeferredCausetDef) (uint64, error) { 3684 // Disallow add/drop actions on auto_random. 3685 var oldRandBits uint64 3686 if blockInfo.PKIsHandle && (blockInfo.GetPkName().L == originDefCaus.Name.L) { 3687 oldRandBits = blockInfo.AutoRandomBits 3688 } 3689 newRandBits, err := extractAutoRandomBitsFromDefCausDef(specNewDeferredCauset) 3690 if err != nil { 3691 return 0, errors.Trace(err) 3692 } 3693 switch { 3694 case oldRandBits == newRandBits: 3695 break 3696 case oldRandBits == 0 || newRandBits == 0: 3697 return 0, ErrInvalidAutoRandom.GenWithStackByArgs(autoid.AutoRandomAlterErrMsg) 3698 case autoid.MaxAutoRandomBits < newRandBits: 3699 errMsg := fmt.Sprintf(autoid.AutoRandomOverflowErrMsg, 3700 autoid.MaxAutoRandomBits, newRandBits, specNewDeferredCauset.Name.Name.O) 3701 return 0, ErrInvalidAutoRandom.GenWithStackByArgs(errMsg) 3702 case oldRandBits < newRandBits: 3703 break // Increasing auto_random shard bits is allowed. 3704 case oldRandBits > newRandBits: 3705 return 0, ErrInvalidAutoRandom.GenWithStackByArgs(autoid.AutoRandomDecreaseBitErrMsg) 3706 } 3707 3708 if oldRandBits != 0 { 3709 // Disallow changing the defCausumn field type. 3710 if originDefCaus.Tp != specNewDeferredCauset.Tp.Tp { 3711 return 0, ErrInvalidAutoRandom.GenWithStackByArgs(autoid.AutoRandomModifyDefCausTypeErrMsg) 3712 } 3713 // Disallow changing auto_increment on auto_random defCausumn. 3714 if containsDeferredCausetOption(specNewDeferredCauset, ast.DeferredCausetOptionAutoIncrement) != allegrosql.HasAutoIncrementFlag(originDefCaus.Flag) { 3715 return 0, ErrInvalidAutoRandom.GenWithStackByArgs(autoid.AutoRandomIncompatibleWithAutoIncErrMsg) 3716 } 3717 // Disallow specifying a default value on auto_random defCausumn. 3718 if containsDeferredCausetOption(specNewDeferredCauset, ast.DeferredCausetOptionDefaultValue) { 3719 return 0, ErrInvalidAutoRandom.GenWithStackByArgs(autoid.AutoRandomIncompatibleWithDefaultValueErrMsg) 3720 } 3721 } 3722 return newRandBits, nil 3723 } 3724 3725 // ChangeDeferredCauset renames an existing defCausumn and modifies the defCausumn's definition, 3726 // currently we only support limited HoTT of changes 3727 // that do not need to change or check data on the causet. 3728 func (d *dbs) ChangeDeferredCauset(ctx stochastikctx.Context, ident ast.Ident, spec *ast.AlterTableSpec) error { 3729 specNewDeferredCauset := spec.NewDeferredCausets[0] 3730 if len(specNewDeferredCauset.Name.Schema.O) != 0 && ident.Schema.L != specNewDeferredCauset.Name.Schema.L { 3731 return ErrWrongDBName.GenWithStackByArgs(specNewDeferredCauset.Name.Schema.O) 3732 } 3733 if len(spec.OldDeferredCausetName.Schema.O) != 0 && ident.Schema.L != spec.OldDeferredCausetName.Schema.L { 3734 return ErrWrongDBName.GenWithStackByArgs(spec.OldDeferredCausetName.Schema.O) 3735 } 3736 if len(specNewDeferredCauset.Name.Block.O) != 0 && ident.Name.L != specNewDeferredCauset.Name.Block.L { 3737 return ErrWrongTableName.GenWithStackByArgs(specNewDeferredCauset.Name.Block.O) 3738 } 3739 if len(spec.OldDeferredCausetName.Block.O) != 0 && ident.Name.L != spec.OldDeferredCausetName.Block.L { 3740 return ErrWrongTableName.GenWithStackByArgs(spec.OldDeferredCausetName.Block.O) 3741 } 3742 3743 job, err := d.getModifiableDeferredCausetJob(ctx, ident, spec.OldDeferredCausetName.Name, spec) 3744 if err != nil { 3745 if schemareplicant.ErrDeferredCausetNotExists.Equal(err) && spec.IfExists { 3746 ctx.GetStochastikVars().StmtCtx.AppendNote(schemareplicant.ErrDeferredCausetNotExists.GenWithStackByArgs(spec.OldDeferredCausetName.Name, ident.Name)) 3747 return nil 3748 } 3749 return errors.Trace(err) 3750 } 3751 3752 err = d.doDBSJob(ctx, job) 3753 // defCausumn not exists, but if_exists flags is true, so we ignore this error. 3754 if schemareplicant.ErrDeferredCausetNotExists.Equal(err) && spec.IfExists { 3755 ctx.GetStochastikVars().StmtCtx.AppendNote(err) 3756 return nil 3757 } 3758 err = d.callHookOnChanged(err) 3759 return errors.Trace(err) 3760 } 3761 3762 // RenameDeferredCauset renames an existing defCausumn. 3763 func (d *dbs) RenameDeferredCauset(ctx stochastikctx.Context, ident ast.Ident, spec *ast.AlterTableSpec) error { 3764 oldDefCausName := spec.OldDeferredCausetName.Name 3765 newDefCausName := spec.NewDeferredCausetName.Name 3766 if oldDefCausName.L == newDefCausName.L { 3767 return nil 3768 } 3769 if newDefCausName.L == perceptron.ExtraHandleName.L { 3770 return ErrWrongDeferredCausetName.GenWithStackByArgs(newDefCausName.L) 3771 } 3772 3773 schemaReplicant, tbl, err := d.getSchemaAndTableByIdent(ctx, ident) 3774 if err != nil { 3775 return errors.Trace(err) 3776 } 3777 3778 oldDefCaus := causet.FindDefCaus(tbl.VisibleDefCauss(), oldDefCausName.L) 3779 if oldDefCaus == nil { 3780 return schemareplicant.ErrDeferredCausetNotExists.GenWithStackByArgs(oldDefCausName, ident.Name) 3781 } 3782 3783 allDefCauss := tbl.DefCauss() 3784 defCausWithNewNameAlreadyExist := causet.FindDefCaus(allDefCauss, newDefCausName.L) != nil 3785 if defCausWithNewNameAlreadyExist { 3786 return schemareplicant.ErrDeferredCausetExists.GenWithStackByArgs(newDefCausName) 3787 } 3788 3789 if fkInfo := getDeferredCausetForeignKeyInfo(oldDefCausName.L, tbl.Meta().ForeignKeys); fkInfo != nil { 3790 return errFKIncompatibleDeferredCausets.GenWithStackByArgs(oldDefCausName, fkInfo.Name) 3791 } 3792 3793 // Check generated memex. 3794 for _, defCaus := range allDefCauss { 3795 if defCaus.GeneratedExpr == nil { 3796 continue 3797 } 3798 dependedDefCausNames := findDeferredCausetNamesInExpr(defCaus.GeneratedExpr) 3799 for _, name := range dependedDefCausNames { 3800 if name.Name.L == oldDefCausName.L { 3801 return ErrBadField.GenWithStackByArgs(oldDefCausName.O, "generated defCausumn function") 3802 } 3803 } 3804 } 3805 3806 newDefCaus := oldDefCaus.Clone() 3807 newDefCaus.Name = newDefCausName 3808 job := &perceptron.Job{ 3809 SchemaID: schemaReplicant.ID, 3810 TableID: tbl.Meta().ID, 3811 SchemaName: schemaReplicant.Name.L, 3812 Type: perceptron.CausetActionModifyDeferredCauset, 3813 BinlogInfo: &perceptron.HistoryInfo{}, 3814 Args: []interface{}{&newDefCaus, oldDefCausName, spec.Position, 0}, 3815 } 3816 err = d.doDBSJob(ctx, job) 3817 err = d.callHookOnChanged(err) 3818 return errors.Trace(err) 3819 } 3820 3821 // ModifyDeferredCauset does modification on an existing defCausumn, currently we only support limited HoTT of changes 3822 // that do not need to change or check data on the causet. 3823 func (d *dbs) ModifyDeferredCauset(ctx stochastikctx.Context, ident ast.Ident, spec *ast.AlterTableSpec) error { 3824 specNewDeferredCauset := spec.NewDeferredCausets[0] 3825 if len(specNewDeferredCauset.Name.Schema.O) != 0 && ident.Schema.L != specNewDeferredCauset.Name.Schema.L { 3826 return ErrWrongDBName.GenWithStackByArgs(specNewDeferredCauset.Name.Schema.O) 3827 } 3828 if len(specNewDeferredCauset.Name.Block.O) != 0 && ident.Name.L != specNewDeferredCauset.Name.Block.L { 3829 return ErrWrongTableName.GenWithStackByArgs(specNewDeferredCauset.Name.Block.O) 3830 } 3831 3832 originalDefCausName := specNewDeferredCauset.Name.Name 3833 job, err := d.getModifiableDeferredCausetJob(ctx, ident, originalDefCausName, spec) 3834 if err != nil { 3835 if schemareplicant.ErrDeferredCausetNotExists.Equal(err) && spec.IfExists { 3836 ctx.GetStochastikVars().StmtCtx.AppendNote(schemareplicant.ErrDeferredCausetNotExists.GenWithStackByArgs(originalDefCausName, ident.Name)) 3837 return nil 3838 } 3839 return errors.Trace(err) 3840 } 3841 3842 err = d.doDBSJob(ctx, job) 3843 // defCausumn not exists, but if_exists flags is true, so we ignore this error. 3844 if schemareplicant.ErrDeferredCausetNotExists.Equal(err) && spec.IfExists { 3845 ctx.GetStochastikVars().StmtCtx.AppendNote(err) 3846 return nil 3847 } 3848 err = d.callHookOnChanged(err) 3849 return errors.Trace(err) 3850 } 3851 3852 func (d *dbs) AlterDeferredCauset(ctx stochastikctx.Context, ident ast.Ident, spec *ast.AlterTableSpec) error { 3853 specNewDeferredCauset := spec.NewDeferredCausets[0] 3854 is := d.infoHandle.Get() 3855 schemaReplicant, ok := is.SchemaByName(ident.Schema) 3856 if !ok { 3857 return schemareplicant.ErrTableNotExists.GenWithStackByArgs(ident.Schema, ident.Name) 3858 } 3859 t, err := is.TableByName(ident.Schema, ident.Name) 3860 if err != nil { 3861 return schemareplicant.ErrTableNotExists.GenWithStackByArgs(ident.Schema, ident.Name) 3862 } 3863 3864 defCausName := specNewDeferredCauset.Name.Name 3865 // Check whether alter defCausumn has existed. 3866 defCaus := causet.FindDefCaus(t.DefCauss(), defCausName.L) 3867 if defCaus == nil { 3868 return ErrBadField.GenWithStackByArgs(defCausName, ident.Name) 3869 } 3870 3871 // Clean the NoDefaultValueFlag value. 3872 defCaus.Flag &= ^allegrosql.NoDefaultValueFlag 3873 if len(specNewDeferredCauset.Options) == 0 { 3874 err = defCaus.SetDefaultValue(nil) 3875 if err != nil { 3876 return errors.Trace(err) 3877 } 3878 setNoDefaultValueFlag(defCaus, false) 3879 } else { 3880 if IsAutoRandomDeferredCausetID(t.Meta(), defCaus.ID) { 3881 return ErrInvalidAutoRandom.GenWithStackByArgs(autoid.AutoRandomIncompatibleWithDefaultValueErrMsg) 3882 } 3883 hasDefaultValue, err := setDefaultValue(ctx, defCaus, specNewDeferredCauset.Options[0]) 3884 if err != nil { 3885 return errors.Trace(err) 3886 } 3887 if err = checkDefaultValue(ctx, defCaus, hasDefaultValue); err != nil { 3888 return errors.Trace(err) 3889 } 3890 } 3891 3892 job := &perceptron.Job{ 3893 SchemaID: schemaReplicant.ID, 3894 TableID: t.Meta().ID, 3895 SchemaName: schemaReplicant.Name.L, 3896 Type: perceptron.CausetActionSetDefaultValue, 3897 BinlogInfo: &perceptron.HistoryInfo{}, 3898 Args: []interface{}{defCaus}, 3899 } 3900 3901 err = d.doDBSJob(ctx, job) 3902 err = d.callHookOnChanged(err) 3903 return errors.Trace(err) 3904 } 3905 3906 // AlterTableComment uFIDelates the causet comment information. 3907 func (d *dbs) AlterTableComment(ctx stochastikctx.Context, ident ast.Ident, spec *ast.AlterTableSpec) error { 3908 is := d.infoHandle.Get() 3909 schemaReplicant, ok := is.SchemaByName(ident.Schema) 3910 if !ok { 3911 return schemareplicant.ErrDatabaseNotExists.GenWithStackByArgs(ident.Schema) 3912 } 3913 3914 tb, err := is.TableByName(ident.Schema, ident.Name) 3915 if err != nil { 3916 return errors.Trace(schemareplicant.ErrTableNotExists.GenWithStackByArgs(ident.Schema, ident.Name)) 3917 } 3918 3919 job := &perceptron.Job{ 3920 SchemaID: schemaReplicant.ID, 3921 TableID: tb.Meta().ID, 3922 SchemaName: schemaReplicant.Name.L, 3923 Type: perceptron.CausetActionModifyTableComment, 3924 BinlogInfo: &perceptron.HistoryInfo{}, 3925 Args: []interface{}{spec.Comment}, 3926 } 3927 3928 err = d.doDBSJob(ctx, job) 3929 err = d.callHookOnChanged(err) 3930 return errors.Trace(err) 3931 } 3932 3933 // AlterTableAutoIDCache uFIDelates the causet comment information. 3934 func (d *dbs) AlterTableAutoIDCache(ctx stochastikctx.Context, ident ast.Ident, newCache int64) error { 3935 schemaReplicant, tb, err := d.getSchemaAndTableByIdent(ctx, ident) 3936 if err != nil { 3937 return errors.Trace(err) 3938 } 3939 3940 job := &perceptron.Job{ 3941 SchemaID: schemaReplicant.ID, 3942 TableID: tb.Meta().ID, 3943 SchemaName: schemaReplicant.Name.L, 3944 Type: perceptron.CausetActionModifyTableAutoIdCache, 3945 BinlogInfo: &perceptron.HistoryInfo{}, 3946 Args: []interface{}{newCache}, 3947 } 3948 3949 err = d.doDBSJob(ctx, job) 3950 err = d.callHookOnChanged(err) 3951 return errors.Trace(err) 3952 } 3953 3954 // AlterTableCharsetAndDefCauslate changes the causet charset and defCauslate. 3955 func (d *dbs) AlterTableCharsetAndDefCauslate(ctx stochastikctx.Context, ident ast.Ident, toCharset, toDefCauslate string, needsOverwriteDefCauss bool) error { 3956 // use the last one. 3957 if toCharset == "" && toDefCauslate == "" { 3958 return ErrUnknownCharacterSet.GenWithStackByArgs(toCharset) 3959 } 3960 3961 is := d.infoHandle.Get() 3962 schemaReplicant, ok := is.SchemaByName(ident.Schema) 3963 if !ok { 3964 return schemareplicant.ErrDatabaseNotExists.GenWithStackByArgs(ident.Schema) 3965 } 3966 3967 tb, err := is.TableByName(ident.Schema, ident.Name) 3968 if err != nil { 3969 return errors.Trace(schemareplicant.ErrTableNotExists.GenWithStackByArgs(ident.Schema, ident.Name)) 3970 } 3971 3972 if toCharset == "" { 3973 // charset does not change. 3974 toCharset = tb.Meta().Charset 3975 } 3976 3977 if toDefCauslate == "" { 3978 // get the default defCauslation of the charset. 3979 toDefCauslate, err = charset.GetDefaultDefCauslation(toCharset) 3980 if err != nil { 3981 return errors.Trace(err) 3982 } 3983 } 3984 doNothing, err := checkAlterTableCharset(tb.Meta(), schemaReplicant, toCharset, toDefCauslate, needsOverwriteDefCauss) 3985 if err != nil { 3986 return err 3987 } 3988 if doNothing { 3989 return nil 3990 } 3991 3992 job := &perceptron.Job{ 3993 SchemaID: schemaReplicant.ID, 3994 TableID: tb.Meta().ID, 3995 SchemaName: schemaReplicant.Name.L, 3996 Type: perceptron.CausetActionModifyTableCharsetAndDefCauslate, 3997 BinlogInfo: &perceptron.HistoryInfo{}, 3998 Args: []interface{}{toCharset, toDefCauslate, needsOverwriteDefCauss}, 3999 } 4000 err = d.doDBSJob(ctx, job) 4001 err = d.callHookOnChanged(err) 4002 return errors.Trace(err) 4003 } 4004 4005 // AlterTableSetTiFlashReplica sets the TiFlash replicas info. 4006 func (d *dbs) AlterTableSetTiFlashReplica(ctx stochastikctx.Context, ident ast.Ident, replicaInfo *ast.TiFlashReplicaSpec) error { 4007 schemaReplicant, tb, err := d.getSchemaAndTableByIdent(ctx, ident) 4008 if err != nil { 4009 return errors.Trace(err) 4010 } 4011 4012 tbReplicaInfo := tb.Meta().TiFlashReplica 4013 if tbReplicaInfo != nil && tbReplicaInfo.Count == replicaInfo.Count && 4014 len(tbReplicaInfo.LocationLabels) == len(replicaInfo.Labels) { 4015 changed := false 4016 for i, lable := range tbReplicaInfo.LocationLabels { 4017 if replicaInfo.Labels[i] != lable { 4018 changed = true 4019 break 4020 } 4021 } 4022 if !changed { 4023 return nil 4024 } 4025 } 4026 4027 err = checkTiFlashReplicaCount(ctx, replicaInfo.Count) 4028 if err != nil { 4029 return errors.Trace(err) 4030 } 4031 4032 job := &perceptron.Job{ 4033 SchemaID: schemaReplicant.ID, 4034 TableID: tb.Meta().ID, 4035 SchemaName: schemaReplicant.Name.L, 4036 Type: perceptron.CausetActionSetTiFlashReplica, 4037 BinlogInfo: &perceptron.HistoryInfo{}, 4038 Args: []interface{}{*replicaInfo}, 4039 } 4040 err = d.doDBSJob(ctx, job) 4041 err = d.callHookOnChanged(err) 4042 return errors.Trace(err) 4043 } 4044 4045 func checkTiFlashReplicaCount(ctx stochastikctx.Context, replicaCount uint64) error { 4046 // Check the tiflash replica count should be less than the total tiflash stores. 4047 tiflashStoreCnt, err := schemareplicant.GetTiFlashStoreCount(ctx) 4048 if err != nil { 4049 return errors.Trace(err) 4050 } 4051 if replicaCount > tiflashStoreCnt { 4052 return errors.Errorf("the tiflash replica count: %d should be less than the total tiflash server count: %d", replicaCount, tiflashStoreCnt) 4053 } 4054 return nil 4055 } 4056 4057 // UFIDelateTableReplicaInfo uFIDelates the causet flash replica infos. 4058 func (d *dbs) UFIDelateTableReplicaInfo(ctx stochastikctx.Context, physicalID int64, available bool) error { 4059 is := d.infoHandle.Get() 4060 tb, ok := is.TableByID(physicalID) 4061 if !ok { 4062 tb, _ = is.FindTableByPartitionID(physicalID) 4063 if tb == nil { 4064 return schemareplicant.ErrTableNotExists.GenWithStack("Block which ID = %d does not exist.", physicalID) 4065 } 4066 } 4067 tbInfo := tb.Meta() 4068 if tbInfo.TiFlashReplica == nil || (tbInfo.ID == physicalID && tbInfo.TiFlashReplica.Available == available) || 4069 (tbInfo.ID != physicalID && available == tbInfo.TiFlashReplica.IsPartitionAvailable(physicalID)) { 4070 return nil 4071 } 4072 4073 EDB, ok := is.SchemaByTable(tbInfo) 4074 if !ok { 4075 return schemareplicant.ErrDatabaseNotExists.GenWithStack("Database of causet `%s` does not exist.", tb.Meta().Name) 4076 } 4077 4078 job := &perceptron.Job{ 4079 SchemaID: EDB.ID, 4080 TableID: tb.Meta().ID, 4081 SchemaName: EDB.Name.L, 4082 Type: perceptron.CausetActionUFIDelateTiFlashReplicaStatus, 4083 BinlogInfo: &perceptron.HistoryInfo{}, 4084 Args: []interface{}{available, physicalID}, 4085 } 4086 err := d.doDBSJob(ctx, job) 4087 err = d.callHookOnChanged(err) 4088 return errors.Trace(err) 4089 } 4090 4091 // checkAlterTableCharset uses to check is it possible to change the charset of causet. 4092 // This function returns 2 variable: 4093 // doNothing: if doNothing is true, means no need to change any more, because the target charset is same with the charset of causet. 4094 // err: if err is not nil, means it is not possible to change causet charset to target charset. 4095 func checkAlterTableCharset(tblInfo *perceptron.TableInfo, dbInfo *perceptron.DBInfo, toCharset, toDefCauslate string, needsOverwriteDefCauss bool) (doNothing bool, err error) { 4096 origCharset := tblInfo.Charset 4097 origDefCauslate := tblInfo.DefCauslate 4098 // Old version schemaReplicant charset maybe modified when load schemaReplicant if TreatOldVersionUTF8AsUTF8MB4 was enable. 4099 // So even if the origCharset equal toCharset, we still need to do the dbs for old version schemaReplicant. 4100 if origCharset == toCharset && origDefCauslate == toDefCauslate && tblInfo.Version >= perceptron.TableInfoVersion2 { 4101 // nothing to do. 4102 doNothing = true 4103 for _, defCaus := range tblInfo.DeferredCausets { 4104 if defCaus.Charset == charset.CharsetBin { 4105 continue 4106 } 4107 if defCaus.Charset == toCharset && defCaus.DefCauslate == toDefCauslate { 4108 continue 4109 } 4110 doNothing = false 4111 } 4112 if doNothing { 4113 return doNothing, nil 4114 } 4115 } 4116 4117 // The causet charset may be "", if the causet is create in old MilevaDB version, such as v2.0.8. 4118 // This DBS will uFIDelate the causet charset to default charset. 4119 origCharset, origDefCauslate, err = ResolveCharsetDefCauslation( 4120 ast.CharsetOpt{Chs: origCharset, DefCaus: origDefCauslate}, 4121 ast.CharsetOpt{Chs: dbInfo.Charset, DefCaus: dbInfo.DefCauslate}, 4122 ) 4123 if err != nil { 4124 return doNothing, err 4125 } 4126 4127 if err = checkModifyCharsetAndDefCauslation(toCharset, toDefCauslate, origCharset, origDefCauslate, false); err != nil { 4128 return doNothing, err 4129 } 4130 if !needsOverwriteDefCauss { 4131 // If we don't change the charset and defCauslation of defCausumns, skip the next checks. 4132 return doNothing, nil 4133 } 4134 4135 for _, defCaus := range tblInfo.DeferredCausets { 4136 if defCaus.Tp == allegrosql.TypeVarchar { 4137 if err = IsTooBigFieldLength(defCaus.Flen, defCaus.Name.O, toCharset); err != nil { 4138 return doNothing, err 4139 } 4140 } 4141 if defCaus.Charset == charset.CharsetBin { 4142 continue 4143 } 4144 if len(defCaus.Charset) == 0 { 4145 continue 4146 } 4147 if err = checkModifyCharsetAndDefCauslation(toCharset, toDefCauslate, defCaus.Charset, defCaus.DefCauslate, isDeferredCausetWithIndex(defCaus.Name.L, tblInfo.Indices)); err != nil { 4148 if strings.Contains(err.Error(), "Unsupported modifying defCauslation") { 4149 defCausErrMsg := "Unsupported converting defCauslation of defCausumn '%s' from '%s' to '%s' when index is defined on it." 4150 err = errUnsupportedModifyDefCauslation.GenWithStack(defCausErrMsg, defCaus.Name.L, defCaus.DefCauslate, toDefCauslate) 4151 } 4152 return doNothing, err 4153 } 4154 } 4155 return doNothing, nil 4156 } 4157 4158 // RenameIndex renames an index. 4159 // In MilevaDB, indexes are case-insensitive (so index 'a' and 'A" are considered the same index), 4160 // but index names are case-sensitive (we can rename index 'a' to 'A') 4161 func (d *dbs) RenameIndex(ctx stochastikctx.Context, ident ast.Ident, spec *ast.AlterTableSpec) error { 4162 is := d.infoHandle.Get() 4163 schemaReplicant, ok := is.SchemaByName(ident.Schema) 4164 if !ok { 4165 return schemareplicant.ErrDatabaseNotExists.GenWithStackByArgs(ident.Schema) 4166 } 4167 4168 tb, err := is.TableByName(ident.Schema, ident.Name) 4169 if err != nil { 4170 return errors.Trace(schemareplicant.ErrTableNotExists.GenWithStackByArgs(ident.Schema, ident.Name)) 4171 } 4172 duplicate, err := validateRenameIndex(spec.FromKey, spec.ToKey, tb.Meta()) 4173 if duplicate { 4174 return nil 4175 } 4176 if err != nil { 4177 return errors.Trace(err) 4178 } 4179 4180 job := &perceptron.Job{ 4181 SchemaID: schemaReplicant.ID, 4182 TableID: tb.Meta().ID, 4183 SchemaName: schemaReplicant.Name.L, 4184 Type: perceptron.CausetActionRenameIndex, 4185 BinlogInfo: &perceptron.HistoryInfo{}, 4186 Args: []interface{}{spec.FromKey, spec.ToKey}, 4187 } 4188 4189 err = d.doDBSJob(ctx, job) 4190 err = d.callHookOnChanged(err) 4191 return errors.Trace(err) 4192 } 4193 4194 // DropTable will proceed even if some causet in the list does not exists. 4195 func (d *dbs) DropTable(ctx stochastikctx.Context, ti ast.Ident) (err error) { 4196 schemaReplicant, tb, err := d.getSchemaAndTableByIdent(ctx, ti) 4197 if err != nil { 4198 return errors.Trace(err) 4199 } 4200 4201 if tb.Meta().IsView() { 4202 return schemareplicant.ErrTableNotExists.GenWithStackByArgs(ti.Schema, ti.Name) 4203 } 4204 if tb.Meta().IsSequence() { 4205 return schemareplicant.ErrTableNotExists.GenWithStackByArgs(ti.Schema, ti.Name) 4206 } 4207 4208 job := &perceptron.Job{ 4209 SchemaID: schemaReplicant.ID, 4210 TableID: tb.Meta().ID, 4211 SchemaName: schemaReplicant.Name.L, 4212 Type: perceptron.CausetActionDropTable, 4213 BinlogInfo: &perceptron.HistoryInfo{}, 4214 } 4215 4216 err = d.doDBSJob(ctx, job) 4217 err = d.callHookOnChanged(err) 4218 if err != nil { 4219 return errors.Trace(err) 4220 } 4221 if !config.TableLockEnabled() { 4222 return nil 4223 } 4224 if ok, _ := ctx.CheckTableLocked(tb.Meta().ID); ok { 4225 ctx.ReleaseTableLockByTableIDs([]int64{tb.Meta().ID}) 4226 } 4227 return nil 4228 } 4229 4230 // DropView will proceed even if some view in the list does not exists. 4231 func (d *dbs) DropView(ctx stochastikctx.Context, ti ast.Ident) (err error) { 4232 schemaReplicant, tb, err := d.getSchemaAndTableByIdent(ctx, ti) 4233 if err != nil { 4234 return errors.Trace(err) 4235 } 4236 4237 if !tb.Meta().IsView() { 4238 return ErrWrongObject.GenWithStackByArgs(ti.Schema, ti.Name, "VIEW") 4239 } 4240 4241 job := &perceptron.Job{ 4242 SchemaID: schemaReplicant.ID, 4243 TableID: tb.Meta().ID, 4244 SchemaName: schemaReplicant.Name.L, 4245 Type: perceptron.CausetActionDropView, 4246 BinlogInfo: &perceptron.HistoryInfo{}, 4247 } 4248 4249 err = d.doDBSJob(ctx, job) 4250 err = d.callHookOnChanged(err) 4251 return errors.Trace(err) 4252 } 4253 4254 func (d *dbs) TruncateTable(ctx stochastikctx.Context, ti ast.Ident) error { 4255 schemaReplicant, tb, err := d.getSchemaAndTableByIdent(ctx, ti) 4256 if err != nil { 4257 return errors.Trace(err) 4258 } 4259 if tb.Meta().IsView() || tb.Meta().IsSequence() { 4260 return schemareplicant.ErrTableNotExists.GenWithStackByArgs(schemaReplicant.Name.O, tb.Meta().Name.O) 4261 } 4262 genIDs, err := d.genGlobalIDs(1) 4263 if err != nil { 4264 return errors.Trace(err) 4265 } 4266 newTableID := genIDs[0] 4267 job := &perceptron.Job{ 4268 SchemaID: schemaReplicant.ID, 4269 TableID: tb.Meta().ID, 4270 SchemaName: schemaReplicant.Name.L, 4271 Type: perceptron.CausetActionTruncateTable, 4272 BinlogInfo: &perceptron.HistoryInfo{}, 4273 Args: []interface{}{newTableID}, 4274 } 4275 if ok, _ := ctx.CheckTableLocked(tb.Meta().ID); ok && config.TableLockEnabled() { 4276 // AddTableLock here to avoid this dbs job was executed successfully but the stochastik was been kill before return. 4277 // The stochastik will release all causet locks it holds, if we don't add the new locking causet id here, 4278 // the stochastik may forget to release the new locked causet id when this dbs job was executed successfully 4279 // but the stochastik was killed before return. 4280 ctx.AddTableLock([]perceptron.TableLockTpInfo{{SchemaID: schemaReplicant.ID, TableID: newTableID, Tp: tb.Meta().Lock.Tp}}) 4281 } 4282 err = d.doDBSJob(ctx, job) 4283 err = d.callHookOnChanged(err) 4284 if err != nil { 4285 if config.TableLockEnabled() { 4286 ctx.ReleaseTableLockByTableIDs([]int64{newTableID}) 4287 } 4288 return errors.Trace(err) 4289 } 4290 oldTblInfo := tb.Meta() 4291 if oldTblInfo.PreSplitRegions > 0 { 4292 if _, tb, err := d.getSchemaAndTableByIdent(ctx, ti); err == nil { 4293 d.preSplitAndScatter(ctx, tb.Meta(), tb.Meta().GetPartitionInfo()) 4294 } 4295 } 4296 4297 if !config.TableLockEnabled() { 4298 return nil 4299 } 4300 if ok, _ := ctx.CheckTableLocked(tb.Meta().ID); ok { 4301 ctx.ReleaseTableLockByTableIDs([]int64{tb.Meta().ID}) 4302 } 4303 return nil 4304 } 4305 4306 func (d *dbs) RenameTable(ctx stochastikctx.Context, oldIdent, newIdent ast.Ident, isAlterTable bool) error { 4307 is := d.GetSchemaReplicantWithInterceptor(ctx) 4308 oldSchema, ok := is.SchemaByName(oldIdent.Schema) 4309 if !ok { 4310 if isAlterTable { 4311 return schemareplicant.ErrTableNotExists.GenWithStackByArgs(oldIdent.Schema, oldIdent.Name) 4312 } 4313 if is.TableExists(newIdent.Schema, newIdent.Name) { 4314 return schemareplicant.ErrTableExists.GenWithStackByArgs(newIdent) 4315 } 4316 return errFileNotFound.GenWithStackByArgs(oldIdent.Schema, oldIdent.Name) 4317 } 4318 oldTbl, err := is.TableByName(oldIdent.Schema, oldIdent.Name) 4319 if err != nil { 4320 if isAlterTable { 4321 return schemareplicant.ErrTableNotExists.GenWithStackByArgs(oldIdent.Schema, oldIdent.Name) 4322 } 4323 if is.TableExists(newIdent.Schema, newIdent.Name) { 4324 return schemareplicant.ErrTableExists.GenWithStackByArgs(newIdent) 4325 } 4326 return errFileNotFound.GenWithStackByArgs(oldIdent.Schema, oldIdent.Name) 4327 } 4328 if isAlterTable && newIdent.Schema.L == oldIdent.Schema.L && newIdent.Name.L == oldIdent.Name.L { 4329 // oldIdent is equal to newIdent, do nothing 4330 return nil 4331 } 4332 newSchema, ok := is.SchemaByName(newIdent.Schema) 4333 if !ok { 4334 return ErrErrorOnRename.GenWithStackByArgs( 4335 fmt.Sprintf("%s.%s", oldIdent.Schema, oldIdent.Name), 4336 fmt.Sprintf("%s.%s", newIdent.Schema, newIdent.Name), 4337 168, 4338 fmt.Sprintf("Database `%s` doesn't exist", newIdent.Schema)) 4339 } 4340 if is.TableExists(newIdent.Schema, newIdent.Name) { 4341 return schemareplicant.ErrTableExists.GenWithStackByArgs(newIdent) 4342 } 4343 if err := checkTooLongTable(newIdent.Name); err != nil { 4344 return errors.Trace(err) 4345 } 4346 4347 job := &perceptron.Job{ 4348 SchemaID: newSchema.ID, 4349 TableID: oldTbl.Meta().ID, 4350 SchemaName: newSchema.Name.L, 4351 Type: perceptron.CausetActionRenameTable, 4352 BinlogInfo: &perceptron.HistoryInfo{}, 4353 Args: []interface{}{oldSchema.ID, newIdent.Name}, 4354 } 4355 4356 err = d.doDBSJob(ctx, job) 4357 err = d.callHookOnChanged(err) 4358 return errors.Trace(err) 4359 } 4360 4361 func getAnonymousIndex(t causet.Block, defCausName perceptron.CIStr) perceptron.CIStr { 4362 id := 2 4363 l := len(t.Indices()) 4364 indexName := defCausName 4365 if strings.EqualFold(indexName.L, allegrosql.PrimaryKeyName) { 4366 indexName = perceptron.NewCIStr(fmt.Sprintf("%s_%d", defCausName.O, id)) 4367 id = 3 4368 } 4369 for i := 0; i < l; i++ { 4370 if t.Indices()[i].Meta().Name.L == indexName.L { 4371 indexName = perceptron.NewCIStr(fmt.Sprintf("%s_%d", defCausName.O, id)) 4372 i = -1 4373 id++ 4374 } 4375 } 4376 return indexName 4377 } 4378 4379 func (d *dbs) CreatePrimaryKey(ctx stochastikctx.Context, ti ast.Ident, indexName perceptron.CIStr, 4380 indexPartSpecifications []*ast.IndexPartSpecification, indexOption *ast.IndexOption) error { 4381 if !config.GetGlobalConfig().AlterPrimaryKey { 4382 return ErrUnsupportedModifyPrimaryKey.GenWithStack("Unsupported add primary key, alter-primary-key is false") 4383 } 4384 4385 schemaReplicant, t, err := d.getSchemaAndTableByIdent(ctx, ti) 4386 if err != nil { 4387 return errors.Trace(err) 4388 } 4389 4390 if err = checkTooLongIndex(indexName); err != nil { 4391 return ErrTooLongIdent.GenWithStackByArgs(allegrosql.PrimaryKeyName) 4392 } 4393 4394 indexName = perceptron.NewCIStr(allegrosql.PrimaryKeyName) 4395 if indexInfo := t.Meta().FindIndexByName(indexName.L); indexInfo != nil || 4396 // If the causet's PKIsHandle is true, it also means that this causet has a primary key. 4397 t.Meta().PKIsHandle { 4398 return schemareplicant.ErrMultiplePriKey 4399 } 4400 4401 // Primary keys cannot include memex index parts. A primary key requires the generated defCausumn to be stored, 4402 // but memex index parts are implemented as virtual generated defCausumns, not stored generated defCausumns. 4403 for _, idxPart := range indexPartSpecifications { 4404 if idxPart.Expr != nil { 4405 return ErrFunctionalIndexPrimaryKey 4406 } 4407 } 4408 4409 tblInfo := t.Meta() 4410 // Check before the job is put to the queue. 4411 // This check is redundant, but useful. If DBS check fail before the job is put 4412 // to job queue, the fail path logic is super fast. 4413 // After DBS job is put to the queue, and if the check fail, MilevaDB will run the DBS cancel logic. 4414 // The recover step causes DBS wait a few seconds, makes the unit test painfully slow. 4415 // For same reason, decide whether index is global here. 4416 indexDeferredCausets, err := buildIndexDeferredCausets(tblInfo.DeferredCausets, indexPartSpecifications) 4417 if err != nil { 4418 return errors.Trace(err) 4419 } 4420 if _, err = checkPKOnGeneratedDeferredCauset(tblInfo, indexPartSpecifications); err != nil { 4421 return err 4422 } 4423 4424 global := false 4425 if tblInfo.GetPartitionInfo() != nil { 4426 ck, err := checkPartitionKeysConstraint(tblInfo.GetPartitionInfo(), indexDeferredCausets, tblInfo) 4427 if err != nil { 4428 return err 4429 } 4430 if !ck { 4431 if !config.GetGlobalConfig().EnableGlobalIndex { 4432 return ErrUniqueKeyNeedAllFieldsInPf.GenWithStackByArgs("PRIMARY") 4433 } 4434 //index defCausumns does not contain all partition defCausumns, must set global 4435 global = true 4436 } 4437 } 4438 4439 // May be truncate comment here, when index comment too long and sql_mode is't strict. 4440 if _, err = validateCommentLength(ctx.GetStochastikVars(), indexName.String(), indexOption); err != nil { 4441 return errors.Trace(err) 4442 } 4443 4444 unique := true 4445 sqlMode := ctx.GetStochastikVars().ALLEGROSQLMode 4446 job := &perceptron.Job{ 4447 SchemaID: schemaReplicant.ID, 4448 TableID: t.Meta().ID, 4449 SchemaName: schemaReplicant.Name.L, 4450 Type: perceptron.CausetActionAddPrimaryKey, 4451 BinlogInfo: &perceptron.HistoryInfo{}, 4452 Args: []interface{}{unique, indexName, indexPartSpecifications, indexOption, sqlMode, nil, global}, 4453 Priority: ctx.GetStochastikVars().DBSReorgPriority, 4454 } 4455 4456 err = d.doDBSJob(ctx, job) 4457 err = d.callHookOnChanged(err) 4458 return errors.Trace(err) 4459 } 4460 4461 func buildHiddenDeferredCausetInfo(ctx stochastikctx.Context, indexPartSpecifications []*ast.IndexPartSpecification, indexName perceptron.CIStr, tblInfo *perceptron.TableInfo, existDefCauss []*causet.DeferredCauset) ([]*perceptron.DeferredCausetInfo, error) { 4462 hiddenDefCauss := make([]*perceptron.DeferredCausetInfo, 0, len(indexPartSpecifications)) 4463 for i, idxPart := range indexPartSpecifications { 4464 if idxPart.Expr == nil { 4465 continue 4466 } 4467 idxPart.DeferredCauset = &ast.DeferredCausetName{Name: perceptron.NewCIStr(fmt.Sprintf("%s_%s_%d", memexIndexPrefix, indexName, i))} 4468 // Check whether the hidden defCausumns have existed. 4469 defCaus := causet.FindDefCaus(existDefCauss, idxPart.DeferredCauset.Name.L) 4470 if defCaus != nil { 4471 // TODO: Use memex index related error. 4472 return nil, schemareplicant.ErrDeferredCausetExists.GenWithStackByArgs(defCaus.Name.String()) 4473 } 4474 idxPart.Length = types.UnspecifiedLength 4475 // The index part is an memex, prepare a hidden defCausumn for it. 4476 if len(idxPart.DeferredCauset.Name.L) > allegrosql.MaxDeferredCausetNameLength { 4477 // TODO: Refine the error message. 4478 return nil, ErrTooLongIdent.GenWithStackByArgs("hidden defCausumn") 4479 } 4480 // TODO: refine the error message. 4481 if err := checkIllegalFn4GeneratedDeferredCauset("memex index", idxPart.Expr); err != nil { 4482 return nil, errors.Trace(err) 4483 } 4484 4485 var sb strings.Builder 4486 restoreFlags := format.RestoreStringSingleQuotes | format.RestoreKeyWordLowercase | format.RestoreNameBackQuotes | 4487 format.RestoreSpacesAroundBinaryOperation 4488 restoreCtx := format.NewRestoreCtx(restoreFlags, &sb) 4489 sb.Reset() 4490 err := idxPart.Expr.Restore(restoreCtx) 4491 if err != nil { 4492 return nil, errors.Trace(err) 4493 } 4494 expr, err := memex.RewriteSimpleExprWithTableInfo(ctx, tblInfo, idxPart.Expr) 4495 if err != nil { 4496 // TODO: refine the error message. 4497 return nil, err 4498 } 4499 if _, ok := expr.(*memex.DeferredCauset); ok { 4500 return nil, ErrFunctionalIndexOnField 4501 } 4502 4503 defCausInfo := &perceptron.DeferredCausetInfo{ 4504 Name: idxPart.DeferredCauset.Name, 4505 GeneratedExprString: sb.String(), 4506 GeneratedStored: false, 4507 Version: perceptron.CurrLatestDeferredCausetInfoVersion, 4508 Dependences: make(map[string]struct{}), 4509 Hidden: true, 4510 FieldType: *expr.GetType(), 4511 } 4512 checkDependencies := make(map[string]struct{}) 4513 for _, defCausName := range findDeferredCausetNamesInExpr(idxPart.Expr) { 4514 defCausInfo.Dependences[defCausName.Name.O] = struct{}{} 4515 checkDependencies[defCausName.Name.O] = struct{}{} 4516 } 4517 if err = checkDependedDefCausExist(checkDependencies, existDefCauss); err != nil { 4518 return nil, errors.Trace(err) 4519 } 4520 if err = checkAutoIncrementRef("", defCausInfo.Dependences, tblInfo); err != nil { 4521 return nil, errors.Trace(err) 4522 } 4523 idxPart.Expr = nil 4524 hiddenDefCauss = append(hiddenDefCauss, defCausInfo) 4525 } 4526 return hiddenDefCauss, nil 4527 } 4528 4529 func (d *dbs) CreateIndex(ctx stochastikctx.Context, ti ast.Ident, keyType ast.IndexKeyType, indexName perceptron.CIStr, 4530 indexPartSpecifications []*ast.IndexPartSpecification, indexOption *ast.IndexOption, ifNotExists bool) error { 4531 // not support Spatial and FullText index 4532 if keyType == ast.IndexKeyTypeFullText || keyType == ast.IndexKeyTypeSpatial { 4533 return errUnsupportedIndexType.GenWithStack("FULLTEXT and SPATIAL index is not supported") 4534 } 4535 unique := keyType == ast.IndexKeyTypeUnique 4536 schemaReplicant, t, err := d.getSchemaAndTableByIdent(ctx, ti) 4537 if err != nil { 4538 return errors.Trace(err) 4539 } 4540 4541 // Deal with anonymous index. 4542 if len(indexName.L) == 0 { 4543 defCausName := perceptron.NewCIStr("memex_index") 4544 if indexPartSpecifications[0].DeferredCauset != nil { 4545 defCausName = indexPartSpecifications[0].DeferredCauset.Name 4546 } 4547 indexName = getAnonymousIndex(t, defCausName) 4548 } 4549 4550 if indexInfo := t.Meta().FindIndexByName(indexName.L); indexInfo != nil { 4551 if indexInfo.State != perceptron.StatePublic { 4552 // NOTE: explicit error message. See issue #18363. 4553 err = ErrDupKeyName.GenWithStack("index already exist %s; "+ 4554 "a background job is trying to add the same index, "+ 4555 "please check by `ADMIN SHOW DBS JOBS`", indexName) 4556 } else { 4557 err = ErrDupKeyName.GenWithStack("index already exist %s", indexName) 4558 } 4559 if ifNotExists { 4560 ctx.GetStochastikVars().StmtCtx.AppendNote(err) 4561 return nil 4562 } 4563 return err 4564 } 4565 4566 if err = checkTooLongIndex(indexName); err != nil { 4567 return errors.Trace(err) 4568 } 4569 4570 tblInfo := t.Meta() 4571 4572 // Build hidden defCausumns if necessary. 4573 hiddenDefCauss, err := buildHiddenDeferredCausetInfo(ctx, indexPartSpecifications, indexName, t.Meta(), t.DefCauss()) 4574 if err != nil { 4575 return err 4576 } 4577 if err = checkAddDeferredCausetTooManyDeferredCausets(len(t.DefCauss()) + len(hiddenDefCauss)); err != nil { 4578 return errors.Trace(err) 4579 } 4580 4581 // Check before the job is put to the queue. 4582 // This check is redundant, but useful. If DBS check fail before the job is put 4583 // to job queue, the fail path logic is super fast. 4584 // After DBS job is put to the queue, and if the check fail, MilevaDB will run the DBS cancel logic. 4585 // The recover step causes DBS wait a few seconds, makes the unit test painfully slow. 4586 // For same reason, decide whether index is global here. 4587 indexDeferredCausets, err := buildIndexDeferredCausets(append(tblInfo.DeferredCausets, hiddenDefCauss...), indexPartSpecifications) 4588 if err != nil { 4589 return errors.Trace(err) 4590 } 4591 4592 global := false 4593 if unique && tblInfo.GetPartitionInfo() != nil { 4594 ck, err := checkPartitionKeysConstraint(tblInfo.GetPartitionInfo(), indexDeferredCausets, tblInfo) 4595 if err != nil { 4596 return err 4597 } 4598 if !ck { 4599 if !config.GetGlobalConfig().EnableGlobalIndex { 4600 return ErrUniqueKeyNeedAllFieldsInPf.GenWithStackByArgs("UNIQUE INDEX") 4601 } 4602 //index defCausumns does not contain all partition defCausumns, must set global 4603 global = true 4604 } 4605 } 4606 // May be truncate comment here, when index comment too long and sql_mode is't strict. 4607 if _, err = validateCommentLength(ctx.GetStochastikVars(), indexName.String(), indexOption); err != nil { 4608 return errors.Trace(err) 4609 } 4610 job := &perceptron.Job{ 4611 SchemaID: schemaReplicant.ID, 4612 TableID: t.Meta().ID, 4613 SchemaName: schemaReplicant.Name.L, 4614 Type: perceptron.CausetActionAddIndex, 4615 BinlogInfo: &perceptron.HistoryInfo{}, 4616 Args: []interface{}{unique, indexName, indexPartSpecifications, indexOption, hiddenDefCauss, global}, 4617 Priority: ctx.GetStochastikVars().DBSReorgPriority, 4618 } 4619 4620 err = d.doDBSJob(ctx, job) 4621 // key exists, but if_not_exists flags is true, so we ignore this error. 4622 if ErrDupKeyName.Equal(err) && ifNotExists { 4623 ctx.GetStochastikVars().StmtCtx.AppendNote(err) 4624 return nil 4625 } 4626 err = d.callHookOnChanged(err) 4627 return errors.Trace(err) 4628 } 4629 4630 func buildFKInfo(fkName perceptron.CIStr, keys []*ast.IndexPartSpecification, refer *ast.ReferenceDef, defcaus []*causet.DeferredCauset, tbInfo *perceptron.TableInfo) (*perceptron.FKInfo, error) { 4631 if len(keys) != len(refer.IndexPartSpecifications) { 4632 return nil, schemareplicant.ErrForeignKeyNotMatch.GenWithStackByArgs("foreign key without name") 4633 } 4634 4635 // all base defCausumns of stored generated defCausumns 4636 baseDefCauss := make(map[string]struct{}) 4637 for _, defCaus := range defcaus { 4638 if defCaus.IsGenerated() && defCaus.GeneratedStored { 4639 for name := range defCaus.Dependences { 4640 baseDefCauss[name] = struct{}{} 4641 } 4642 } 4643 } 4644 4645 fkInfo := &perceptron.FKInfo{ 4646 Name: fkName, 4647 RefTable: refer.Block.Name, 4648 DefCauss: make([]perceptron.CIStr, len(keys)), 4649 } 4650 4651 for i, key := range keys { 4652 // Check add foreign key to generated defCausumns 4653 // For more detail, see https://dev.allegrosql.com/doc/refman/8.0/en/innodb-foreign-key-constraints.html#innodb-foreign-key-generated-defCausumns 4654 for _, defCaus := range defcaus { 4655 if defCaus.Name.L != key.DeferredCauset.Name.L { 4656 continue 4657 } 4658 if defCaus.IsGenerated() { 4659 // Check foreign key on virtual generated defCausumns 4660 if !defCaus.GeneratedStored { 4661 return nil, schemareplicant.ErrCannotAddForeign 4662 } 4663 4664 // Check wrong reference options of foreign key on stored generated defCausumns 4665 switch refer.OnUFIDelate.ReferOpt { 4666 case ast.ReferOptionCascade, ast.ReferOptionSetNull, ast.ReferOptionSetDefault: 4667 return nil, errWrongFKOptionForGeneratedDeferredCauset.GenWithStackByArgs("ON UFIDelATE " + refer.OnUFIDelate.ReferOpt.String()) 4668 } 4669 switch refer.OnDelete.ReferOpt { 4670 case ast.ReferOptionSetNull, ast.ReferOptionSetDefault: 4671 return nil, errWrongFKOptionForGeneratedDeferredCauset.GenWithStackByArgs("ON DELETE " + refer.OnDelete.ReferOpt.String()) 4672 } 4673 continue 4674 } 4675 // Check wrong reference options of foreign key on base defCausumns of stored generated defCausumns 4676 if _, ok := baseDefCauss[defCaus.Name.L]; ok { 4677 switch refer.OnUFIDelate.ReferOpt { 4678 case ast.ReferOptionCascade, ast.ReferOptionSetNull, ast.ReferOptionSetDefault: 4679 return nil, schemareplicant.ErrCannotAddForeign 4680 } 4681 switch refer.OnDelete.ReferOpt { 4682 case ast.ReferOptionCascade, ast.ReferOptionSetNull, ast.ReferOptionSetDefault: 4683 return nil, schemareplicant.ErrCannotAddForeign 4684 } 4685 } 4686 } 4687 if causet.FindDefCaus(defcaus, key.DeferredCauset.Name.O) == nil { 4688 return nil, errKeyDeferredCausetDoesNotExits.GenWithStackByArgs(key.DeferredCauset.Name) 4689 } 4690 fkInfo.DefCauss[i] = key.DeferredCauset.Name 4691 } 4692 4693 fkInfo.RefDefCauss = make([]perceptron.CIStr, len(refer.IndexPartSpecifications)) 4694 for i, key := range refer.IndexPartSpecifications { 4695 fkInfo.RefDefCauss[i] = key.DeferredCauset.Name 4696 } 4697 4698 fkInfo.OnDelete = int(refer.OnDelete.ReferOpt) 4699 fkInfo.OnUFIDelate = int(refer.OnUFIDelate.ReferOpt) 4700 4701 return fkInfo, nil 4702 } 4703 4704 func (d *dbs) CreateForeignKey(ctx stochastikctx.Context, ti ast.Ident, fkName perceptron.CIStr, keys []*ast.IndexPartSpecification, refer *ast.ReferenceDef) error { 4705 is := d.infoHandle.Get() 4706 schemaReplicant, ok := is.SchemaByName(ti.Schema) 4707 if !ok { 4708 return schemareplicant.ErrDatabaseNotExists.GenWithStackByArgs(ti.Schema) 4709 } 4710 4711 t, err := is.TableByName(ti.Schema, ti.Name) 4712 if err != nil { 4713 return errors.Trace(schemareplicant.ErrTableNotExists.GenWithStackByArgs(ti.Schema, ti.Name)) 4714 } 4715 4716 fkInfo, err := buildFKInfo(fkName, keys, refer, t.DefCauss(), t.Meta()) 4717 if err != nil { 4718 return errors.Trace(err) 4719 } 4720 4721 job := &perceptron.Job{ 4722 SchemaID: schemaReplicant.ID, 4723 TableID: t.Meta().ID, 4724 SchemaName: schemaReplicant.Name.L, 4725 Type: perceptron.CausetActionAddForeignKey, 4726 BinlogInfo: &perceptron.HistoryInfo{}, 4727 Args: []interface{}{fkInfo}, 4728 } 4729 4730 err = d.doDBSJob(ctx, job) 4731 err = d.callHookOnChanged(err) 4732 return errors.Trace(err) 4733 4734 } 4735 4736 func (d *dbs) DropForeignKey(ctx stochastikctx.Context, ti ast.Ident, fkName perceptron.CIStr) error { 4737 is := d.infoHandle.Get() 4738 schemaReplicant, ok := is.SchemaByName(ti.Schema) 4739 if !ok { 4740 return schemareplicant.ErrDatabaseNotExists.GenWithStackByArgs(ti.Schema) 4741 } 4742 4743 t, err := is.TableByName(ti.Schema, ti.Name) 4744 if err != nil { 4745 return errors.Trace(schemareplicant.ErrTableNotExists.GenWithStackByArgs(ti.Schema, ti.Name)) 4746 } 4747 4748 job := &perceptron.Job{ 4749 SchemaID: schemaReplicant.ID, 4750 TableID: t.Meta().ID, 4751 SchemaName: schemaReplicant.Name.L, 4752 Type: perceptron.CausetActionDropForeignKey, 4753 BinlogInfo: &perceptron.HistoryInfo{}, 4754 Args: []interface{}{fkName}, 4755 } 4756 4757 err = d.doDBSJob(ctx, job) 4758 err = d.callHookOnChanged(err) 4759 return errors.Trace(err) 4760 } 4761 4762 func (d *dbs) DropIndex(ctx stochastikctx.Context, ti ast.Ident, indexName perceptron.CIStr, ifExists bool) error { 4763 is := d.infoHandle.Get() 4764 schemaReplicant, ok := is.SchemaByName(ti.Schema) 4765 if !ok { 4766 return errors.Trace(schemareplicant.ErrDatabaseNotExists) 4767 } 4768 t, err := is.TableByName(ti.Schema, ti.Name) 4769 if err != nil { 4770 return errors.Trace(schemareplicant.ErrTableNotExists.GenWithStackByArgs(ti.Schema, ti.Name)) 4771 } 4772 4773 indexInfo := t.Meta().FindIndexByName(indexName.L) 4774 var isPK bool 4775 if indexName.L == strings.ToLower(allegrosql.PrimaryKeyName) && 4776 // Before we fixed #14243, there might be a general index named `primary` but not a primary key. 4777 (indexInfo == nil || indexInfo.Primary) { 4778 isPK = true 4779 } 4780 if isPK { 4781 if !config.GetGlobalConfig().AlterPrimaryKey { 4782 return ErrUnsupportedModifyPrimaryKey.GenWithStack("Unsupported drop primary key when alter-primary-key is false") 4783 4784 } 4785 // If the causet's PKIsHandle is true, we can't find the index from the causet. So we check the value of PKIsHandle. 4786 if indexInfo == nil && !t.Meta().PKIsHandle { 4787 return ErrCantDropFieldOrKey.GenWithStack("Can't DROP 'PRIMARY'; check that defCausumn/key exists") 4788 } 4789 if t.Meta().PKIsHandle { 4790 return ErrUnsupportedModifyPrimaryKey.GenWithStack("Unsupported drop primary key when the causet's pkIsHandle is true") 4791 } 4792 if t.Meta().IsCommonHandle { 4793 return ErrUnsupportedModifyPrimaryKey.GenWithStack("Unsupported drop primary key when the causet is using clustered index") 4794 } 4795 } 4796 if indexInfo == nil { 4797 err = ErrCantDropFieldOrKey.GenWithStack("index %s doesn't exist", indexName) 4798 if ifExists { 4799 ctx.GetStochastikVars().StmtCtx.AppendNote(err) 4800 return nil 4801 } 4802 return err 4803 } 4804 4805 // Check for drop index on auto_increment defCausumn. 4806 err = checkDropIndexOnAutoIncrementDeferredCauset(t.Meta(), indexInfo) 4807 if err != nil { 4808 return errors.Trace(err) 4809 } 4810 4811 jobTp := perceptron.CausetActionDropIndex 4812 if isPK { 4813 jobTp = perceptron.CausetActionDropPrimaryKey 4814 } 4815 4816 job := &perceptron.Job{ 4817 SchemaID: schemaReplicant.ID, 4818 TableID: t.Meta().ID, 4819 SchemaName: schemaReplicant.Name.L, 4820 Type: jobTp, 4821 BinlogInfo: &perceptron.HistoryInfo{}, 4822 Args: []interface{}{indexName}, 4823 } 4824 4825 err = d.doDBSJob(ctx, job) 4826 // index not exists, but if_exists flags is true, so we ignore this error. 4827 if ErrCantDropFieldOrKey.Equal(err) && ifExists { 4828 ctx.GetStochastikVars().StmtCtx.AppendNote(err) 4829 return nil 4830 } 4831 err = d.callHookOnChanged(err) 4832 return errors.Trace(err) 4833 } 4834 4835 func isDroppableDeferredCauset(tblInfo *perceptron.TableInfo, defCausName perceptron.CIStr) error { 4836 // Check whether there are other defCausumns depend on this defCausumn or not. 4837 for _, defCaus := range tblInfo.DeferredCausets { 4838 for dep := range defCaus.Dependences { 4839 if dep == defCausName.L { 4840 return errDependentByGeneratedDeferredCauset.GenWithStackByArgs(dep) 4841 } 4842 } 4843 } 4844 if len(tblInfo.DeferredCausets) == 1 { 4845 return ErrCantRemoveAllFields.GenWithStack("can't drop only defCausumn %s in causet %s", 4846 defCausName, tblInfo.Name) 4847 } 4848 // We only support dropping defCausumn with single-value none Primary Key index covered now. 4849 if !isDeferredCausetCanDropWithIndex(defCausName.L, tblInfo.Indices) { 4850 return errCantDropDefCausWithIndex.GenWithStack("can't drop defCausumn %s with composite index covered or Primary Key covered now", defCausName) 4851 } 4852 // Check the defCausumn with foreign key. 4853 if fkInfo := getDeferredCausetForeignKeyInfo(defCausName.L, tblInfo.ForeignKeys); fkInfo != nil { 4854 return errFkDeferredCausetCannotDrop.GenWithStackByArgs(defCausName, fkInfo.Name) 4855 } 4856 return nil 4857 } 4858 4859 // validateCommentLength checks comment length of causet, defCausumn, index and partition. 4860 // If comment length is more than the standard length truncate it 4861 // and causetstore the comment length upto the standard comment length size. 4862 func validateCommentLength(vars *variable.StochastikVars, indexName string, indexOption *ast.IndexOption) (string, error) { 4863 if indexOption == nil { 4864 return "", nil 4865 } 4866 4867 maxLen := MaxCommentLength 4868 if len(indexOption.Comment) > maxLen { 4869 err := errTooLongIndexComment.GenWithStackByArgs(indexName, maxLen) 4870 if vars.StrictALLEGROSQLMode { 4871 return "", err 4872 } 4873 vars.StmtCtx.AppendWarning(err) 4874 indexOption.Comment = indexOption.Comment[:maxLen] 4875 } 4876 return indexOption.Comment, nil 4877 } 4878 4879 func buildPartitionInfo(ctx stochastikctx.Context, spacetime *perceptron.TableInfo, d *dbs, spec *ast.AlterTableSpec) (*perceptron.PartitionInfo, error) { 4880 if spacetime.Partition.Type == perceptron.PartitionTypeRange { 4881 if len(spec.PartDefinitions) == 0 { 4882 return nil, ast.ErrPartitionsMustBeDefined.GenWithStackByArgs(spacetime.Partition.Type) 4883 } 4884 } else { 4885 // we don't support ADD PARTITION for all other partition types yet. 4886 return nil, errors.Trace(ErrUnsupportedAddPartition) 4887 } 4888 4889 part := &perceptron.PartitionInfo{ 4890 Type: spacetime.Partition.Type, 4891 Expr: spacetime.Partition.Expr, 4892 DeferredCausets: spacetime.Partition.DeferredCausets, 4893 Enable: spacetime.Partition.Enable, 4894 } 4895 4896 genIDs, err := d.genGlobalIDs(len(spec.PartDefinitions)) 4897 if err != nil { 4898 return nil, err 4899 } 4900 for ith, def := range spec.PartDefinitions { 4901 if err := def.Clause.Validate(part.Type, len(part.DeferredCausets)); err != nil { 4902 return nil, errors.Trace(err) 4903 } 4904 if err := checkTooLongTable(def.Name); err != nil { 4905 return nil, err 4906 } 4907 // For RANGE partition only VALUES LESS THAN should be possible. 4908 clause := def.Clause.(*ast.PartitionDefinitionClauseLessThan) 4909 if len(part.DeferredCausets) > 0 { 4910 if err := checkRangeDeferredCausetsTypeAndValuesMatch(ctx, spacetime, clause.Exprs); err != nil { 4911 return nil, err 4912 } 4913 } 4914 4915 comment, _ := def.Comment() 4916 piDef := perceptron.PartitionDefinition{ 4917 Name: def.Name, 4918 ID: genIDs[ith], 4919 Comment: comment, 4920 } 4921 4922 buf := new(bytes.Buffer) 4923 for _, expr := range clause.Exprs { 4924 expr.Format(buf) 4925 piDef.LessThan = append(piDef.LessThan, buf.String()) 4926 buf.Reset() 4927 } 4928 part.Definitions = append(part.Definitions, piDef) 4929 } 4930 return part, nil 4931 } 4932 4933 func checkRangeDeferredCausetsTypeAndValuesMatch(ctx stochastikctx.Context, spacetime *perceptron.TableInfo, exprs []ast.ExprNode) error { 4934 // Validate() has already checked len(defCausNames) = len(exprs) 4935 // create causet ... partition by range defCausumns (defcaus) 4936 // partition p0 values less than (expr) 4937 // check the type of defcaus[i] and expr is consistent. 4938 defCausNames := spacetime.Partition.DeferredCausets 4939 for i, defCausExpr := range exprs { 4940 if _, ok := defCausExpr.(*ast.MaxValueExpr); ok { 4941 continue 4942 } 4943 4944 defCausName := defCausNames[i] 4945 defCausInfo := getDeferredCausetInfoByName(spacetime, defCausName.L) 4946 if defCausInfo == nil { 4947 return errors.Trace(ErrFieldNotFoundPart) 4948 } 4949 defCausType := &defCausInfo.FieldType 4950 4951 val, err := memex.EvalAstExpr(ctx, defCausExpr) 4952 if err != nil { 4953 return err 4954 } 4955 4956 // Check val.ConvertTo(defCausType) doesn't work, so we need this case by case check. 4957 switch defCausType.Tp { 4958 case allegrosql.TypeDate, allegrosql.TypeDatetime: 4959 switch val.HoTT() { 4960 case types.HoTTString, types.HoTTBytes: 4961 default: 4962 return ErrWrongTypeDeferredCausetValue.GenWithStackByArgs() 4963 } 4964 } 4965 } 4966 return nil 4967 } 4968 4969 // LockTables uses to execute dagger blocks memex. 4970 func (d *dbs) LockTables(ctx stochastikctx.Context, stmt *ast.LockTablesStmt) error { 4971 lockTables := make([]perceptron.TableLockTpInfo, 0, len(stmt.TableLocks)) 4972 stochastikInfo := perceptron.StochastikInfo{ 4973 ServerID: d.GetID(), 4974 StochastikID: ctx.GetStochastikVars().ConnectionID, 4975 } 4976 uniqueTableID := make(map[int64]struct{}) 4977 // Check whether the causet was already locked by another. 4978 for _, tl := range stmt.TableLocks { 4979 tb := tl.Block 4980 err := throwErrIfInMemOrSysDB(ctx, tb.Schema.L) 4981 if err != nil { 4982 return err 4983 } 4984 schemaReplicant, t, err := d.getSchemaAndTableByIdent(ctx, ast.Ident{Schema: tb.Schema, Name: tb.Name}) 4985 if err != nil { 4986 return errors.Trace(err) 4987 } 4988 if t.Meta().IsView() || t.Meta().IsSequence() { 4989 return causet.ErrUnsupportedOp.GenWithStackByArgs() 4990 } 4991 err = checkTableLocked(t.Meta(), tl.Type, stochastikInfo) 4992 if err != nil { 4993 return err 4994 } 4995 if _, ok := uniqueTableID[t.Meta().ID]; ok { 4996 return schemareplicant.ErrNonuniqTable.GenWithStackByArgs(t.Meta().Name) 4997 } 4998 uniqueTableID[t.Meta().ID] = struct{}{} 4999 lockTables = append(lockTables, perceptron.TableLockTpInfo{SchemaID: schemaReplicant.ID, TableID: t.Meta().ID, Tp: tl.Type}) 5000 } 5001 5002 unlockTables := ctx.GetAllTableLocks() 5003 arg := &lockTablesArg{ 5004 LockTables: lockTables, 5005 UnlockTables: unlockTables, 5006 StochastikInfo: stochastikInfo, 5007 } 5008 job := &perceptron.Job{ 5009 SchemaID: lockTables[0].SchemaID, 5010 TableID: lockTables[0].TableID, 5011 Type: perceptron.CausetActionLockTable, 5012 BinlogInfo: &perceptron.HistoryInfo{}, 5013 Args: []interface{}{arg}, 5014 } 5015 // AddTableLock here is avoiding this job was executed successfully but the stochastik was killed before return. 5016 ctx.AddTableLock(lockTables) 5017 err := d.doDBSJob(ctx, job) 5018 if err == nil { 5019 ctx.ReleaseTableLocks(unlockTables) 5020 ctx.AddTableLock(lockTables) 5021 } 5022 err = d.callHookOnChanged(err) 5023 return errors.Trace(err) 5024 } 5025 5026 // UnlockTables uses to execute unlock blocks memex. 5027 func (d *dbs) UnlockTables(ctx stochastikctx.Context, unlockTables []perceptron.TableLockTpInfo) error { 5028 if len(unlockTables) == 0 { 5029 return nil 5030 } 5031 arg := &lockTablesArg{ 5032 UnlockTables: unlockTables, 5033 StochastikInfo: perceptron.StochastikInfo{ 5034 ServerID: d.GetID(), 5035 StochastikID: ctx.GetStochastikVars().ConnectionID, 5036 }, 5037 } 5038 job := &perceptron.Job{ 5039 SchemaID: unlockTables[0].SchemaID, 5040 TableID: unlockTables[0].TableID, 5041 Type: perceptron.CausetActionUnlockTable, 5042 BinlogInfo: &perceptron.HistoryInfo{}, 5043 Args: []interface{}{arg}, 5044 } 5045 5046 err := d.doDBSJob(ctx, job) 5047 if err == nil { 5048 ctx.ReleaseAllTableLocks() 5049 } 5050 err = d.callHookOnChanged(err) 5051 return errors.Trace(err) 5052 } 5053 5054 // CleanDeadTableLock uses to clean dead causet locks. 5055 func (d *dbs) CleanDeadTableLock(unlockTables []perceptron.TableLockTpInfo, se perceptron.StochastikInfo) error { 5056 if len(unlockTables) == 0 { 5057 return nil 5058 } 5059 arg := &lockTablesArg{ 5060 UnlockTables: unlockTables, 5061 StochastikInfo: se, 5062 } 5063 job := &perceptron.Job{ 5064 SchemaID: unlockTables[0].SchemaID, 5065 TableID: unlockTables[0].TableID, 5066 Type: perceptron.CausetActionUnlockTable, 5067 BinlogInfo: &perceptron.HistoryInfo{}, 5068 Args: []interface{}{arg}, 5069 } 5070 5071 ctx, err := d.sessPool.get() 5072 if err != nil { 5073 return err 5074 } 5075 defer d.sessPool.put(ctx) 5076 err = d.doDBSJob(ctx, job) 5077 err = d.callHookOnChanged(err) 5078 return errors.Trace(err) 5079 } 5080 5081 func throwErrIfInMemOrSysDB(ctx stochastikctx.Context, dbLowerName string) error { 5082 if soliton.IsMemOrSysDB(dbLowerName) { 5083 if ctx.GetStochastikVars().User != nil { 5084 return schemareplicant.ErrAccessDenied.GenWithStackByArgs(ctx.GetStochastikVars().User.Username, ctx.GetStochastikVars().User.Hostname) 5085 } 5086 return schemareplicant.ErrAccessDenied.GenWithStackByArgs("", "") 5087 } 5088 return nil 5089 } 5090 5091 func (d *dbs) CleanupTableLock(ctx stochastikctx.Context, blocks []*ast.TableName) error { 5092 uniqueTableID := make(map[int64]struct{}) 5093 cleanupTables := make([]perceptron.TableLockTpInfo, 0, len(blocks)) 5094 unlockedTablesNum := 0 5095 // Check whether the causet was already locked by another. 5096 for _, tb := range blocks { 5097 err := throwErrIfInMemOrSysDB(ctx, tb.Schema.L) 5098 if err != nil { 5099 return err 5100 } 5101 schemaReplicant, t, err := d.getSchemaAndTableByIdent(ctx, ast.Ident{Schema: tb.Schema, Name: tb.Name}) 5102 if err != nil { 5103 return errors.Trace(err) 5104 } 5105 if t.Meta().IsView() || t.Meta().IsSequence() { 5106 return causet.ErrUnsupportedOp 5107 } 5108 // Maybe the causet t was not locked, but still try to unlock this causet. 5109 // If we skip unlock the causet here, the job maybe not consistent with the job.Query. 5110 // eg: unlock blocks t1,t2; If t2 is not locked and skip here, then the job will only unlock causet t1, 5111 // and this behaviour is not consistent with the allegrosql query. 5112 if !t.Meta().IsLocked() { 5113 unlockedTablesNum++ 5114 } 5115 if _, ok := uniqueTableID[t.Meta().ID]; ok { 5116 return schemareplicant.ErrNonuniqTable.GenWithStackByArgs(t.Meta().Name) 5117 } 5118 uniqueTableID[t.Meta().ID] = struct{}{} 5119 cleanupTables = append(cleanupTables, perceptron.TableLockTpInfo{SchemaID: schemaReplicant.ID, TableID: t.Meta().ID}) 5120 } 5121 // If the num of cleanupTables is 0, or all cleanupTables is unlocked, just return here. 5122 if len(cleanupTables) == 0 || len(cleanupTables) == unlockedTablesNum { 5123 return nil 5124 } 5125 5126 arg := &lockTablesArg{ 5127 UnlockTables: cleanupTables, 5128 IsCleanup: true, 5129 } 5130 job := &perceptron.Job{ 5131 SchemaID: cleanupTables[0].SchemaID, 5132 TableID: cleanupTables[0].TableID, 5133 Type: perceptron.CausetActionUnlockTable, 5134 BinlogInfo: &perceptron.HistoryInfo{}, 5135 Args: []interface{}{arg}, 5136 } 5137 err := d.doDBSJob(ctx, job) 5138 if err == nil { 5139 ctx.ReleaseTableLocks(cleanupTables) 5140 } 5141 err = d.callHookOnChanged(err) 5142 return errors.Trace(err) 5143 } 5144 5145 type lockTablesArg struct { 5146 LockTables []perceptron.TableLockTpInfo 5147 IndexOfLock int 5148 UnlockTables []perceptron.TableLockTpInfo 5149 IndexOfUnlock int 5150 StochastikInfo perceptron.StochastikInfo 5151 IsCleanup bool 5152 } 5153 5154 func (d *dbs) RepairTable(ctx stochastikctx.Context, causet *ast.TableName, createStmt *ast.CreateTableStmt) error { 5155 // Existence of EDB and causet has been checked in the preprocessor. 5156 oldTableInfo, ok := (ctx.Value(petriutil.RepairedTable)).(*perceptron.TableInfo) 5157 if !ok || oldTableInfo == nil { 5158 return ErrRepairTableFail.GenWithStack("Failed to get the repaired causet") 5159 } 5160 oldDBInfo, ok := (ctx.Value(petriutil.RepairedDatabase)).(*perceptron.DBInfo) 5161 if !ok || oldDBInfo == nil { 5162 return ErrRepairTableFail.GenWithStack("Failed to get the repaired database") 5163 } 5164 // By now only support same EDB repair. 5165 if createStmt.Block.Schema.L != oldDBInfo.Name.L { 5166 return ErrRepairTableFail.GenWithStack("Repaired causet should in same database with the old one") 5167 } 5168 5169 // It is necessary to specify the causet.ID and partition.ID manually. 5170 newTableInfo, err := buildTableInfoWithCheck(ctx, createStmt, oldTableInfo.Charset, oldTableInfo.DefCauslate) 5171 if err != nil { 5172 return errors.Trace(err) 5173 } 5174 // Override newTableInfo with oldTableInfo's element necessary. 5175 // TODO: There may be more element assignments here, and the new TableInfo should be verified with the actual data. 5176 newTableInfo.ID = oldTableInfo.ID 5177 if err = checkAndOverridePartitionID(newTableInfo, oldTableInfo); err != nil { 5178 return err 5179 } 5180 newTableInfo.AutoIncID = oldTableInfo.AutoIncID 5181 // If any old defCausumnInfo has lost, that means the old defCausumn ID lost too, repair failed. 5182 for i, newOne := range newTableInfo.DeferredCausets { 5183 old := getDeferredCausetInfoByName(oldTableInfo, newOne.Name.L) 5184 if old == nil { 5185 return ErrRepairTableFail.GenWithStackByArgs("DeferredCauset " + newOne.Name.L + " has lost") 5186 } 5187 if newOne.Tp != old.Tp { 5188 return ErrRepairTableFail.GenWithStackByArgs("DeferredCauset " + newOne.Name.L + " type should be the same") 5189 } 5190 if newOne.Flen != old.Flen { 5191 logutil.BgLogger().Warn("[dbs] admin repair causet : DeferredCauset " + newOne.Name.L + " flen is not equal to the old one") 5192 } 5193 newTableInfo.DeferredCausets[i].ID = old.ID 5194 } 5195 // If any old indexInfo has lost, that means the index ID lost too, so did the data, repair failed. 5196 for i, newOne := range newTableInfo.Indices { 5197 old := getIndexInfoByNameAndDeferredCauset(oldTableInfo, newOne) 5198 if old == nil { 5199 return ErrRepairTableFail.GenWithStackByArgs("Index " + newOne.Name.L + " has lost") 5200 } 5201 if newOne.Tp != old.Tp { 5202 return ErrRepairTableFail.GenWithStackByArgs("Index " + newOne.Name.L + " type should be the same") 5203 } 5204 newTableInfo.Indices[i].ID = old.ID 5205 } 5206 5207 newTableInfo.State = perceptron.StatePublic 5208 err = checkTableInfoValid(newTableInfo) 5209 if err != nil { 5210 return err 5211 } 5212 newTableInfo.State = perceptron.StateNone 5213 5214 job := &perceptron.Job{ 5215 SchemaID: oldDBInfo.ID, 5216 TableID: newTableInfo.ID, 5217 SchemaName: oldDBInfo.Name.L, 5218 Type: perceptron.CausetActionRepairTable, 5219 BinlogInfo: &perceptron.HistoryInfo{}, 5220 Args: []interface{}{newTableInfo}, 5221 } 5222 err = d.doDBSJob(ctx, job) 5223 if err == nil { 5224 // Remove the old TableInfo from repairInfo before petri reload. 5225 petriutil.RepairInfo.RemoveFromRepairInfo(oldDBInfo.Name.L, oldTableInfo.Name.L) 5226 } 5227 err = d.callHookOnChanged(err) 5228 return errors.Trace(err) 5229 } 5230 5231 func (d *dbs) OrderByDeferredCausets(ctx stochastikctx.Context, ident ast.Ident) error { 5232 _, tb, err := d.getSchemaAndTableByIdent(ctx, ident) 5233 if err != nil { 5234 return errors.Trace(err) 5235 } 5236 if tb.Meta().GetPkDefCausInfo() != nil { 5237 ctx.GetStochastikVars().StmtCtx.AppendWarning(errors.Errorf("ORDER BY ignored as there is a user-defined clustered index in the causet '%s'", ident.Name)) 5238 } 5239 return nil 5240 } 5241 5242 func (d *dbs) CreateSequence(ctx stochastikctx.Context, stmt *ast.CreateSequenceStmt) error { 5243 ident := ast.Ident{Name: stmt.Name.Name, Schema: stmt.Name.Schema} 5244 sequenceInfo, err := buildSequenceInfo(stmt, ident) 5245 if err != nil { 5246 return err 5247 } 5248 // MilevaDB describe the sequence within a blockInfo, as a same-level object of a causet and view. 5249 tbInfo, err := buildTableInfo(ctx, ident.Name, nil, nil, "", "") 5250 if err != nil { 5251 return err 5252 } 5253 tbInfo.Sequence = sequenceInfo 5254 5255 onExist := OnExistError 5256 if stmt.IfNotExists { 5257 onExist = OnExistIgnore 5258 } 5259 5260 return d.CreateTableWithInfo(ctx, ident.Schema, tbInfo, onExist, false /*tryRetainID*/) 5261 } 5262 5263 func (d *dbs) DropSequence(ctx stochastikctx.Context, ti ast.Ident, ifExists bool) (err error) { 5264 schemaReplicant, tbl, err := d.getSchemaAndTableByIdent(ctx, ti) 5265 if err != nil { 5266 return errors.Trace(err) 5267 } 5268 5269 if !tbl.Meta().IsSequence() { 5270 err = ErrWrongObject.GenWithStackByArgs(ti.Schema, ti.Name, "SEQUENCE") 5271 if ifExists { 5272 ctx.GetStochastikVars().StmtCtx.AppendNote(err) 5273 return nil 5274 } 5275 return err 5276 } 5277 5278 job := &perceptron.Job{ 5279 SchemaID: schemaReplicant.ID, 5280 TableID: tbl.Meta().ID, 5281 SchemaName: schemaReplicant.Name.L, 5282 Type: perceptron.CausetActionDropSequence, 5283 BinlogInfo: &perceptron.HistoryInfo{}, 5284 } 5285 5286 err = d.doDBSJob(ctx, job) 5287 err = d.callHookOnChanged(err) 5288 return errors.Trace(err) 5289 } 5290 5291 func (d *dbs) AlterIndexVisibility(ctx stochastikctx.Context, ident ast.Ident, indexName perceptron.CIStr, visibility ast.IndexVisibility) error { 5292 schemaReplicant, tb, err := d.getSchemaAndTableByIdent(ctx, ident) 5293 if err != nil { 5294 return err 5295 } 5296 5297 invisible := false 5298 if visibility == ast.IndexVisibilityInvisible { 5299 invisible = true 5300 } 5301 5302 skip, err := validateAlterIndexVisibility(indexName, invisible, tb.Meta()) 5303 if err != nil { 5304 return errors.Trace(err) 5305 } 5306 if skip { 5307 return nil 5308 } 5309 5310 job := &perceptron.Job{ 5311 SchemaID: schemaReplicant.ID, 5312 TableID: tb.Meta().ID, 5313 SchemaName: schemaReplicant.Name.L, 5314 Type: perceptron.CausetActionAlterIndexVisibility, 5315 BinlogInfo: &perceptron.HistoryInfo{}, 5316 Args: []interface{}{indexName, invisible}, 5317 } 5318 5319 err = d.doDBSJob(ctx, job) 5320 err = d.callHookOnChanged(err) 5321 return errors.Trace(err) 5322 } 5323 5324 func buildPlacementSpecReplicasAndConstraint(rule *memristed.MemruleOp, replicas uint64, cnstr string) ([]*memristed.MemruleOp, error) { 5325 var err error 5326 cnstr = strings.TrimSpace(cnstr) 5327 rules := make([]*memristed.MemruleOp, 0, 1) 5328 if len(cnstr) > 0 && cnstr[0] == '[' { 5329 // can not emit REPLICAS with an array label 5330 if replicas == 0 { 5331 return rules, errors.Errorf("array CONSTRAINTS should be with a positive REPLICAS") 5332 } 5333 rule.Count = int(replicas) 5334 5335 constraints := []string{} 5336 5337 err = json.Unmarshal([]byte(cnstr), &constraints) 5338 if err != nil { 5339 return rules, err 5340 } 5341 5342 rule.LabelConstraints, err = memristed.CheckLabelConstraints(constraints) 5343 if err != nil { 5344 return rules, err 5345 } 5346 5347 rules = append(rules, rule) 5348 } else if len(cnstr) > 0 && cnstr[0] == '{' { 5349 constraints := map[string]int{} 5350 err = json.Unmarshal([]byte(cnstr), &constraints) 5351 if err != nil { 5352 return rules, err 5353 } 5354 5355 ruleCnt := int(replicas) 5356 for labels, cnt := range constraints { 5357 newMemrule := rule.Clone() 5358 if cnt <= 0 { 5359 err = errors.Errorf("count should be positive, but got %d", cnt) 5360 break 5361 } 5362 5363 if replicas != 0 { 5364 ruleCnt -= cnt 5365 if ruleCnt < 0 { 5366 err = errors.Errorf("REPLICAS should be larger or equal to the number of total replicas, but got %d", replicas) 5367 break 5368 } 5369 } 5370 newMemrule.Count = cnt 5371 5372 newMemrule.LabelConstraints, err = memristed.CheckLabelConstraints(strings.Split(strings.TrimSpace(labels), ",")) 5373 if err != nil { 5374 break 5375 } 5376 rules = append(rules, newMemrule) 5377 } 5378 rule.Count = ruleCnt 5379 5380 if rule.Count > 0 { 5381 rules = append(rules, rule) 5382 } 5383 } else { 5384 err = errors.Errorf("constraint should be a JSON array or object, but got '%s'", cnstr) 5385 } 5386 return rules, err 5387 } 5388 5389 func buildPlacementSpecs(specs []*ast.PlacementSpec) ([]*memristed.MemruleOp, error) { 5390 rules := make([]*memristed.MemruleOp, 0, len(specs)) 5391 5392 var err error 5393 var sb strings.Builder 5394 restoreFlags := format.RestoreStringSingleQuotes | format.RestoreKeyWordLowercase | format.RestoreNameBackQuotes 5395 restoreCtx := format.NewRestoreCtx(restoreFlags, &sb) 5396 5397 for _, spec := range specs { 5398 rule := &memristed.MemruleOp{ 5399 Memrule: &memristed.Memrule{ 5400 GroupID: memristed.MemruleDefaultGroupID, 5401 Override: true, 5402 }, 5403 } 5404 5405 switch spec.Role { 5406 case ast.PlacementRoleFollower: 5407 rule.Role = memristed.Follower 5408 case ast.PlacementRoleLeader: 5409 rule.Role = memristed.Leader 5410 case ast.PlacementRoleLearner: 5411 rule.Role = memristed.Learner 5412 case ast.PlacementRoleVoter: 5413 rule.Role = memristed.Voter 5414 default: 5415 err = errors.Errorf("unknown role: %d", spec.Role) 5416 } 5417 5418 if err == nil { 5419 switch spec.Tp { 5420 case ast.PlacementAdd: 5421 rule.CausetAction = memristed.MemruleOpAdd 5422 case ast.PlacementAlter, ast.PlacementDrop: 5423 rule.CausetAction = memristed.MemruleOpAdd 5424 5425 // alter will overwrite all things 5426 // drop all rules that will be overridden 5427 newMemrules := rules[:0] 5428 5429 for _, r := range rules { 5430 if r.Role != rule.Role { 5431 newMemrules = append(newMemrules, r) 5432 } 5433 } 5434 5435 rules = newMemrules 5436 5437 // delete previous definitions 5438 rules = append(rules, &memristed.MemruleOp{ 5439 CausetAction: memristed.MemruleOFIDelel, 5440 DeleteByIDPrefix: true, 5441 Memrule: &memristed.Memrule{ 5442 GroupID: memristed.MemruleDefaultGroupID, 5443 // ROLE is useless for FIDel, prevent two alter memexs from coexisting 5444 Role: rule.Role, 5445 }, 5446 }) 5447 5448 // alter == drop + add new rules 5449 if spec.Tp == ast.PlacementDrop { 5450 continue 5451 } 5452 default: 5453 err = errors.Errorf("unknown action type: %d", spec.Tp) 5454 } 5455 } 5456 5457 if err == nil { 5458 var newMemrules []*memristed.MemruleOp 5459 newMemrules, err = buildPlacementSpecReplicasAndConstraint(rule, spec.Replicas, spec.Constraints) 5460 rules = append(rules, newMemrules...) 5461 } 5462 5463 if err != nil { 5464 sb.Reset() 5465 if e := spec.Restore(restoreCtx); e != nil { 5466 return rules, ErrInvalidPlacementSpec.GenWithStackByArgs("", err) 5467 } 5468 return rules, ErrInvalidPlacementSpec.GenWithStackByArgs(sb.String(), err) 5469 } 5470 } 5471 return rules, nil 5472 } 5473 5474 func (d *dbs) AlterTablePartition(ctx stochastikctx.Context, ident ast.Ident, spec *ast.AlterTableSpec) (err error) { 5475 schemaReplicant, tb, err := d.getSchemaAndTableByIdent(ctx, ident) 5476 if err != nil { 5477 return errors.Trace(err) 5478 } 5479 5480 spacetime := tb.Meta() 5481 if spacetime.Partition == nil { 5482 return errors.Trace(ErrPartitionMgmtOnNonpartitioned) 5483 } 5484 5485 partitionID, err := blocks.FindPartitionByName(spacetime, spec.PartitionNames[0].L) 5486 if err != nil { 5487 return errors.Trace(err) 5488 } 5489 5490 rules, err := buildPlacementSpecs(spec.PlacementSpecs) 5491 if err != nil { 5492 return errors.Trace(err) 5493 } 5494 5495 startKey := hex.EncodeToString(codec.EncodeBytes(nil, blockcodec.GenTablePrefix(partitionID))) 5496 endKey := hex.EncodeToString(codec.EncodeBytes(nil, blockcodec.GenTablePrefix(partitionID+1))) 5497 for _, rule := range rules { 5498 rule.Index = memristed.MemruleIndexPartition 5499 rule.StartKeyHex = startKey 5500 rule.EndKeyHex = endKey 5501 } 5502 5503 job := &perceptron.Job{ 5504 SchemaID: schemaReplicant.ID, 5505 TableID: spacetime.ID, 5506 SchemaName: schemaReplicant.Name.L, 5507 Type: perceptron.CausetActionAlterTableAlterPartition, 5508 BinlogInfo: &perceptron.HistoryInfo{}, 5509 Args: []interface{}{partitionID, rules}, 5510 } 5511 5512 err = d.doDBSJob(ctx, job) 5513 if err != nil { 5514 return errors.Trace(err) 5515 } 5516 5517 err = d.callHookOnChanged(err) 5518 return errors.Trace(err) 5519 }