github.com/matrixorigin/matrixone@v1.2.0/pkg/sql/plan/build_alter_table.go (about) 1 // Copyright 2023 Matrix Origin 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package plan 16 17 import ( 18 "bytes" 19 "context" 20 "encoding/json" 21 "fmt" 22 "math" 23 "strings" 24 25 "github.com/google/uuid" 26 27 "github.com/matrixorigin/matrixone/pkg/catalog" 28 "github.com/matrixorigin/matrixone/pkg/common/moerr" 29 "github.com/matrixorigin/matrixone/pkg/container/types" 30 "github.com/matrixorigin/matrixone/pkg/pb/plan" 31 "github.com/matrixorigin/matrixone/pkg/pb/timestamp" 32 "github.com/matrixorigin/matrixone/pkg/sql/parsers/tree" 33 "github.com/matrixorigin/matrixone/pkg/sql/util" 34 ) 35 36 func buildAlterTableCopy(stmt *tree.AlterTable, ctx CompilerContext) (*Plan, error) { 37 // 1. get origin table name and Schema name 38 schemaName, tableName := string(stmt.Table.Schema()), string(stmt.Table.Name()) 39 if schemaName == "" { 40 schemaName = ctx.DefaultDatabase() 41 } 42 43 _, tableDef := ctx.Resolve(schemaName, tableName, Snapshot{TS: ×tamp.Timestamp{}}) 44 if tableDef == nil { 45 return nil, moerr.NewNoSuchTable(ctx.GetContext(), schemaName, tableName) 46 } 47 48 isClusterTable := util.TableIsClusterTable(tableDef.GetTableType()) 49 accountId, err := ctx.GetAccountId() 50 if err != nil { 51 return nil, err 52 } 53 if isClusterTable && accountId != catalog.System_Account { 54 return nil, moerr.NewInternalError(ctx.GetContext(), "only the sys account can alter the cluster table") 55 } 56 57 // 2. split alter_option list 58 copyTableDef, err := buildCopyTableDef(ctx.GetContext(), tableDef) 59 if err != nil { 60 return nil, err 61 } 62 alterTableCtx := initAlterTableContext(tableDef, copyTableDef, schemaName) 63 64 // 3. check alter_option list 65 // set name for anonymous foreign key. 66 tmpForeignKeyId := 0 67 validAlterSpecs := stmt.Options 68 for _, spec := range validAlterSpecs { 69 if alterOpt, ok := spec.(*tree.AlterOptionAdd); ok { 70 if foreignKey, ok2 := alterOpt.Def.(*tree.ForeignKey); ok2 && foreignKey.Name == "" { 71 foreignKey.Name = fmt.Sprintf("fk_%d", tmpForeignKeyId) 72 } 73 } 74 } 75 76 // 4. traverse and handle alter options 77 alterTablePlan := &plan.AlterTable{ 78 Database: schemaName, 79 TableDef: tableDef, 80 CopyTableDef: copyTableDef, 81 IsClusterTable: isClusterTable, 82 AlgorithmType: plan.AlterTable_COPY, 83 } 84 85 for _, spec := range validAlterSpecs { 86 switch option := spec.(type) { 87 case *tree.AlterOptionAdd: 88 switch optionAdd := option.Def.(type) { 89 case *tree.PrimaryKeyIndex: 90 err = AddPrimaryKey(ctx, alterTablePlan, optionAdd, alterTableCtx) 91 case *tree.ForeignKey: 92 return nil, moerr.NewInvalidInput(ctx.GetContext(), "Do not support this stmt now. %v", optionAdd) 93 case *tree.UniqueIndex: 94 return nil, moerr.NewInvalidInput(ctx.GetContext(), "Do not support this stmt now. %v", optionAdd) 95 case *tree.Index: 96 return nil, moerr.NewInvalidInput(ctx.GetContext(), "Do not support this stmt now. %v", optionAdd) 97 case *tree.ColumnTableDef: 98 return nil, moerr.NewInvalidInput(ctx.GetContext(), "Do not support this stmt now. %v", optionAdd) 99 default: 100 return nil, moerr.NewInvalidInput(ctx.GetContext(), "Do not support this stmt now. %v", optionAdd) 101 } 102 case *tree.AlterOptionDrop: 103 switch option.Typ { 104 case tree.AlterTableDropColumn: 105 //return nil, moerr.NewInvalidInput(ctx.GetContext(), "Do not support this stmt now. %v", option) 106 err = DropColumn(ctx, alterTablePlan, string(option.Name), alterTableCtx) 107 case tree.AlterTableDropIndex: 108 return nil, moerr.NewInvalidInput(ctx.GetContext(), "Do not support this stmt now. %v", option) 109 case tree.AlterTableDropKey: 110 return nil, moerr.NewInvalidInput(ctx.GetContext(), "Do not support this stmt now. %v", option) 111 case tree.AlterTableDropPrimaryKey: 112 err = DropPrimaryKey(ctx, alterTablePlan, alterTableCtx) 113 case tree.AlterTableDropForeignKey: 114 return nil, moerr.NewInvalidInput(ctx.GetContext(), "Do not support this stmt now. %v", option) 115 default: 116 return nil, moerr.NewInvalidInput(ctx.GetContext(), "Do not support this stmt now. %v", option) 117 } 118 case *tree.AlterOptionAlterIndex: 119 return nil, moerr.NewInvalidInput(ctx.GetContext(), "Do not support this stmt now. %v", spec) 120 case *tree.AlterOptionAlterReIndex: 121 return nil, moerr.NewInvalidInput(ctx.GetContext(), "Do not support this stmt now. %v", spec) 122 case *tree.TableOptionComment: 123 return nil, moerr.NewInvalidInput(ctx.GetContext(), "Do not support this stmt now. %v", spec) 124 case *tree.AlterOptionTableName: 125 return nil, moerr.NewInvalidInput(ctx.GetContext(), "Do not support this stmt now. %v", spec) 126 case *tree.AlterAddCol: 127 err = AddColumn(ctx, alterTablePlan, option, alterTableCtx) 128 case *tree.AlterTableModifyColumnClause: 129 err = ModifyColumn(ctx, alterTablePlan, option, alterTableCtx) 130 case *tree.AlterTableChangeColumnClause: 131 err = ChangeColumn(ctx, alterTablePlan, option, alterTableCtx) 132 case *tree.AlterTableRenameColumnClause: 133 err = RenameColumn(ctx, alterTablePlan, option, alterTableCtx) 134 case *tree.AlterTableAlterColumnClause: 135 err = AlterColumn(ctx, alterTablePlan, option, alterTableCtx) 136 case *tree.AlterTableOrderByColumnClause: 137 err = OrderByColumn(ctx, alterTablePlan, option, alterTableCtx) 138 case *tree.TableOptionAutoIncrement: 139 return nil, moerr.NewInvalidInput(ctx.GetContext(), "Do not support this stmt now. %v", spec) 140 default: 141 return nil, moerr.NewInvalidInput(ctx.GetContext(), "Do not support this stmt now.") 142 } 143 if err != nil { 144 return nil, err 145 } 146 } 147 148 createTmpDdl, err := restoreDDL(ctx, alterTablePlan.CopyTableDef, schemaName, alterTableCtx.copyTableName, false) 149 if err != nil { 150 return nil, err 151 } 152 alterTablePlan.CreateTmpTableSql = createTmpDdl 153 154 createDdl, err := restoreDDL(ctx, alterTablePlan.CopyTableDef, schemaName, alterTableCtx.originTableName, false) 155 if err != nil { 156 return nil, err 157 } 158 alterTablePlan.CreateTableSql = createDdl 159 160 insertTmpDml, err := buildAlterInsertDataSQL(ctx, alterTableCtx) 161 if err != nil { 162 return nil, err 163 } 164 alterTablePlan.InsertTmpDataSql = insertTmpDml 165 166 insertDml, err := builInsertSQL(ctx, alterTableCtx) 167 if err != nil { 168 return nil, err 169 } 170 alterTablePlan.InsertDataSql = insertDml 171 172 alterTablePlan.ChangeTblColIdMap = alterTableCtx.changColDefMap 173 alterTablePlan.UpdateFkSqls = append(alterTablePlan.UpdateFkSqls, alterTableCtx.UpdateSqls...) 174 //delete copy table records from mo_catalog.mo_foreign_keys 175 alterTablePlan.UpdateFkSqls = append(alterTablePlan.UpdateFkSqls, getSqlForDeleteTable(schemaName, alterTableCtx.copyTableName)) 176 return &Plan{ 177 Plan: &plan.Plan_Ddl{ 178 Ddl: &plan.DataDefinition{ 179 DdlType: plan.DataDefinition_ALTER_TABLE, 180 Definition: &plan.DataDefinition_AlterTable{ 181 AlterTable: alterTablePlan, 182 }, 183 }, 184 }, 185 }, nil 186 } 187 188 // restoreDDL Get the DDL statement for the corresponding table based on tableDef, 189 // skipConstraint: Skip foreign key and index constraints 190 func restoreDDL(ctx CompilerContext, tableDef *TableDef, schemaName string, tblName string, skipConstraint bool) (string, error) { 191 var createStr string 192 if tableDef.TableType == catalog.SystemOrdinaryRel { 193 createStr = fmt.Sprintf("CREATE TABLE `%s`.`%s` (", formatStr(schemaName), formatStr(tblName)) 194 } else if tableDef.TableType == catalog.SystemExternalRel { 195 createStr = fmt.Sprintf("CREATE EXTERNAL TABLE `%s`.`%s` (", formatStr(schemaName), formatStr(tblName)) 196 } else if tableDef.TableType == catalog.SystemClusterRel { 197 createStr = fmt.Sprintf("CREATE CLUSTER TABLE `%s`.`%s` (", formatStr(schemaName), formatStr(tblName)) 198 } else if tblName == catalog.MO_DATABASE || tblName == catalog.MO_TABLES || tblName == catalog.MO_COLUMNS { 199 createStr = fmt.Sprintf("CREATE TABLE `%s`.`%s` (", formatStr(schemaName), formatStr(tblName)) 200 } 201 202 rowCount := 0 203 var pkDefs []string 204 isClusterTable := util.TableIsClusterTable(tableDef.TableType) 205 206 colIdToName := make(map[uint64]string) 207 for _, col := range tableDef.Cols { 208 if col.Hidden { 209 continue 210 } 211 colName := col.Name 212 colIdToName[col.ColId] = col.Name 213 if colName == catalog.Row_ID { 214 continue 215 } 216 //the non-sys account skips the column account_id of the cluster table 217 accountId, err := ctx.GetAccountId() 218 if err != nil { 219 return "", err 220 } 221 if util.IsClusterTableAttribute(colName) && 222 isClusterTable && 223 accountId != catalog.System_Account { 224 continue 225 } 226 nullOrNot := "NOT NULL" 227 // col.Default must be not nil 228 if len(col.Default.OriginString) > 0 { 229 nullOrNot = "DEFAULT " + formatStr(col.Default.OriginString) 230 } else if col.Default.NullAbility { 231 nullOrNot = "" 232 } 233 234 if col.Typ.AutoIncr { 235 nullOrNot = "NOT NULL AUTO_INCREMENT" 236 } 237 238 var hasAttrComment string 239 if col.Comment != "" { 240 hasAttrComment = " COMMENT '" + col.Comment + "'" 241 } 242 243 if rowCount == 0 { 244 createStr += "\n" 245 } else { 246 createStr += ",\n" 247 } 248 typ := types.T(col.Typ.Id).ToType() 249 typeStr := typ.String() 250 if typ.Oid.IsDecimal() { //after decimal fix,remove this 251 typeStr = fmt.Sprintf("DECIMAL(%d,%d)", col.Typ.Width, col.Typ.Scale) 252 } 253 if typ.Oid == types.T_varchar || typ.Oid == types.T_char || 254 typ.Oid == types.T_binary || typ.Oid == types.T_varbinary || 255 typ.Oid.IsArrayRelate() || typ.Oid == types.T_bit { 256 typeStr += fmt.Sprintf("(%d)", col.Typ.Width) 257 } 258 if typ.Oid.IsFloat() && col.Typ.Scale != -1 { 259 typeStr += fmt.Sprintf("(%d,%d)", col.Typ.Width, col.Typ.Scale) 260 } 261 262 if typ.Oid.IsEnum() { 263 enumStr := col.GetTyp().Enumvalues 264 enums := strings.Split(enumStr, ",") 265 enumVal := "" 266 for i, enum := range enums { 267 if i == 0 { 268 enumVal += fmt.Sprintf("'%s'", enum) 269 } else { 270 enumVal += fmt.Sprintf(",'%s'", enum) 271 } 272 } 273 typeStr = fmt.Sprintf("ENUM(%s)", enumVal) 274 } 275 276 updateOpt := "" 277 if col.OnUpdate != nil && col.OnUpdate.Expr != nil { 278 updateOpt = " ON UPDATE " + col.OnUpdate.OriginString 279 } 280 createStr += fmt.Sprintf("`%s` %s %s%s%s", formatStr(colName), typeStr, nullOrNot, updateOpt, hasAttrComment) 281 rowCount++ 282 if col.Primary { 283 pkDefs = append(pkDefs, colName) 284 } 285 } 286 287 // If it is a composite primary key, get the component columns of the composite primary key 288 if tableDef.Pkey != nil && len(tableDef.Pkey.Names) > 1 { 289 pkDefs = append(pkDefs, tableDef.Pkey.Names...) 290 } 291 292 if len(pkDefs) != 0 { 293 pkStr := "PRIMARY KEY (" 294 for i, def := range pkDefs { 295 if i == len(pkDefs)-1 { 296 pkStr += fmt.Sprintf("`%s`", formatStr(def)) 297 } else { 298 pkStr += fmt.Sprintf("`%s`,", formatStr(def)) 299 } 300 } 301 pkStr += ")" 302 if rowCount != 0 { 303 createStr += ",\n" 304 } 305 createStr += pkStr 306 } 307 308 if !skipConstraint { 309 if tableDef.Indexes != nil { 310 311 // We only print distinct index names. This is used to avoid printing the same index multiple times for IVFFLAT or 312 // other multi-table indexes. 313 indexNames := make(map[string]bool) 314 315 for _, indexdef := range tableDef.Indexes { 316 if _, ok := indexNames[indexdef.IndexName]; ok { 317 continue 318 } else { 319 indexNames[indexdef.IndexName] = true 320 } 321 322 var indexStr string 323 if indexdef.Unique { 324 indexStr = "UNIQUE KEY " 325 } else { 326 indexStr = "KEY " 327 } 328 indexStr += fmt.Sprintf("`%s` ", formatStr(indexdef.IndexName)) 329 if !catalog.IsNullIndexAlgo(indexdef.IndexAlgo) { 330 indexStr += fmt.Sprintf("USING %s ", indexdef.IndexAlgo) 331 } 332 indexStr += "(" 333 i := 0 334 for _, part := range indexdef.Parts { 335 // NOTE: we skip the alias PK column from the secondary keys list here. 336 // The final SQL string will be similar to the output of "show create table" 337 // (ie buildShowCreateTable) and we should avoid 338 // showing the alias column in the secondary keys list. 339 if catalog.IsAlias(part) { 340 continue 341 } 342 if i > 0 { 343 indexStr += "," 344 } 345 indexStr += fmt.Sprintf("`%s`", formatStr(part)) 346 i++ 347 } 348 indexStr += ")" 349 if indexdef.Comment != "" { 350 indexdef.Comment = strings.Replace(indexdef.Comment, "'", "\\'", -1) 351 indexStr += fmt.Sprintf(" COMMENT '%s'", formatStr(indexdef.Comment)) 352 } 353 if indexdef.IndexAlgoParams != "" { 354 var paramList string 355 var err error 356 paramList, err = catalog.IndexParamsToStringList(indexdef.IndexAlgoParams) 357 if err != nil { 358 return "", err 359 } 360 indexStr += paramList 361 } 362 if rowCount != 0 { 363 createStr += ",\n" 364 } 365 createStr += indexStr 366 } 367 } 368 } 369 370 if !skipConstraint { 371 for _, fk := range tableDef.Fkeys { 372 colNames := make([]string, len(fk.Cols)) 373 for i, colId := range fk.Cols { 374 colNames[i] = colIdToName[colId] 375 } 376 _, fkTableDef := ctx.ResolveById(fk.ForeignTbl, Snapshot{TS: ×tamp.Timestamp{}}) 377 fkColIdToName := make(map[uint64]string) 378 for _, col := range fkTableDef.Cols { 379 fkColIdToName[col.ColId] = col.Name 380 } 381 fkColNames := make([]string, len(fk.ForeignCols)) 382 for i, colId := range fk.ForeignCols { 383 fkColNames[i] = fkColIdToName[colId] 384 } 385 386 if rowCount != 0 { 387 createStr += ",\n" 388 } 389 390 if fk.Name == "" { 391 createStr += fmt.Sprintf("CONSTRAINT FOREIGN KEY (`%s`) REFERENCES `%s` (`%s`) ON DELETE %s ON UPDATE %s", 392 strings.Join(colNames, "`,`"), formatStr(fkTableDef.Name), strings.Join(fkColNames, "`,`"), fk.OnDelete.String(), fk.OnUpdate.String()) 393 } else { 394 createStr += fmt.Sprintf("CONSTRAINT `%s` FOREIGN KEY (`%s`) REFERENCES `%s` (`%s`) ON DELETE %s ON UPDATE %s", 395 formatStr(fk.Name), strings.Join(colNames, "`,`"), formatStr(fkTableDef.Name), strings.Join(fkColNames, "`,`"), fk.OnDelete.String(), fk.OnUpdate.String()) 396 } 397 } 398 } 399 400 if rowCount != 0 { 401 createStr += "\n" 402 } 403 createStr += ")" 404 405 if tableDef.ClusterBy != nil { 406 clusterby := " CLUSTER BY (" 407 if util.JudgeIsCompositeClusterByColumn(tableDef.ClusterBy.Name) { 408 //multi column clusterby 409 cbNames := util.SplitCompositeClusterByColumnName(tableDef.ClusterBy.Name) 410 for i, cbName := range cbNames { 411 if i != 0 { 412 clusterby += fmt.Sprintf(", `%s`", formatStr(cbName)) 413 } else { 414 clusterby += fmt.Sprintf("`%s`", formatStr(cbName)) 415 } 416 } 417 } else { 418 //single column cluster by 419 clusterby += fmt.Sprintf("`%s`", formatStr(tableDef.ClusterBy.Name)) 420 } 421 clusterby += ")" 422 createStr += clusterby 423 } 424 425 var comment string 426 var partition string 427 for _, def := range tableDef.Defs { 428 if proDef, ok := def.Def.(*plan.TableDef_DefType_Properties); ok { 429 for _, kv := range proDef.Properties.Properties { 430 if kv.Key == catalog.SystemRelAttr_Comment { 431 comment = " COMMENT='" + kv.Value + "'" 432 } 433 } 434 } 435 } 436 437 if tableDef.Partition != nil { 438 partition = ` ` + tableDef.Partition.PartitionMsg 439 } 440 441 createStr += comment 442 createStr += partition 443 444 if tableDef.TableType == catalog.SystemExternalRel { 445 param := tree.ExternParam{} 446 err := json.Unmarshal([]byte(tableDef.Createsql), ¶m) 447 if err != nil { 448 return "", err 449 } 450 createStr += fmt.Sprintf(" INFILE{'FILEPATH'='%s','COMPRESSION'='%s','FORMAT'='%s','JSONDATA'='%s'}", param.Filepath, param.CompressType, param.Format, param.JsonData) 451 452 fields := " FIELDS" 453 if param.Tail.Fields.Terminated != nil { 454 if param.Tail.Fields.Terminated.Value == "" { 455 fields += " TERMINATED BY \"\"" 456 } else { 457 fields += fmt.Sprintf(" TERMINATED BY '%s'", param.Tail.Fields.Terminated.Value) 458 } 459 } 460 if param.Tail.Fields.EnclosedBy != nil { 461 if param.Tail.Fields.EnclosedBy.Value == byte(0) { 462 fields += " ENCLOSED BY ''" 463 } else if param.Tail.Fields.EnclosedBy.Value == byte('\\') { 464 fields += " ENCLOSED BY '\\\\'" 465 } else { 466 fields += fmt.Sprintf(" ENCLOSED BY '%c'", param.Tail.Fields.EnclosedBy.Value) 467 } 468 } 469 if param.Tail.Fields.EscapedBy != nil { 470 if param.Tail.Fields.EscapedBy.Value == byte(0) { 471 fields += " ESCAPED BY ''" 472 } else if param.Tail.Fields.EscapedBy.Value == byte('\\') { 473 fields += " ESCAPED BY '\\\\'" 474 } else { 475 fields += fmt.Sprintf(" ESCAPED BY '%c'", param.Tail.Fields.EscapedBy.Value) 476 } 477 } 478 479 line := " LINES" 480 if param.Tail.Lines.StartingBy != "" { 481 line += fmt.Sprintf(" STARTING BY '%s'", param.Tail.Lines.StartingBy) 482 } 483 if param.Tail.Lines.TerminatedBy != nil { 484 if param.Tail.Lines.TerminatedBy.Value == "\n" || param.Tail.Lines.TerminatedBy.Value == "\r\n" { 485 line += " TERMINATED BY '\\\\n'" 486 } else { 487 line += fmt.Sprintf(" TERMINATED BY '%s'", param.Tail.Lines.TerminatedBy) 488 } 489 } 490 491 if fields != " FIELDS" { 492 createStr += fields 493 } 494 if line != " LINES" { 495 createStr += line 496 } 497 498 if param.Tail.IgnoredLines > 0 { 499 createStr += fmt.Sprintf(" IGNORE %d LINES", param.Tail.IgnoredLines) 500 } 501 } 502 503 var buf bytes.Buffer 504 for _, ch := range createStr { 505 if ch == '"' { 506 buf.WriteRune('"') 507 } 508 buf.WriteRune(ch) 509 } 510 sql := buf.String() 511 stmt, err := getRewriteSQLStmt(ctx, sql) 512 defer func() { 513 if stmt != nil { 514 stmt.Free() 515 } 516 }() 517 if err != nil { 518 return "", err 519 } 520 return sql, nil 521 } 522 523 func buildAlterInsertDataSQL(ctx CompilerContext, alterCtx *AlterTableContext) (string, error) { 524 schemaName := alterCtx.schemaName 525 originTableName := alterCtx.originTableName 526 copyTableName := alterCtx.copyTableName 527 528 insertBuffer := bytes.NewBufferString("") 529 selectBuffer := bytes.NewBufferString("") 530 531 isFirst := true 532 for key, value := range alterCtx.alterColMap { 533 if isFirst { 534 insertBuffer.WriteString("`" + key + "`") 535 if value.sexprType == columnName { 536 selectBuffer.WriteString("`" + value.sexprStr + "`") 537 } else { 538 selectBuffer.WriteString(value.sexprStr) 539 } 540 isFirst = false 541 } else { 542 insertBuffer.WriteString(", " + "`" + key + "`") 543 544 if value.sexprType == columnName { 545 selectBuffer.WriteString(", " + "`" + value.sexprStr + "`") 546 } else { 547 selectBuffer.WriteString(", " + value.sexprStr) 548 } 549 } 550 } 551 552 insertSQL := fmt.Sprintf("INSERT INTO `%s`.`%s` (%s) SELECT %s FROM `%s`.`%s`", 553 formatStr(schemaName), formatStr(copyTableName), insertBuffer.String(), 554 selectBuffer.String(), formatStr(schemaName), formatStr(originTableName)) 555 return insertSQL, nil 556 } 557 558 func builInsertSQL(ctx CompilerContext, alterCtx *AlterTableContext) (string, error) { 559 schemaName := alterCtx.schemaName 560 originTableName := alterCtx.originTableName 561 copyTableName := alterCtx.copyTableName 562 563 insertSQL := fmt.Sprintf("INSERT INTO `%s`.`%s` SELECT * FROM `%s`.`%s`", 564 formatStr(schemaName), formatStr(originTableName), formatStr(schemaName), formatStr(copyTableName)) 565 return insertSQL, nil 566 } 567 568 const UnKnownColId uint64 = math.MaxUint64 569 570 type AlterTableContext struct { 571 // key --> Copy table column name 572 // value --> Original table column name 573 alterColMap map[string]selectExpr 574 schemaName string 575 originTableName string 576 copyTableName string 577 // key oldColId -> new ColDef 578 changColDefMap map[uint64]*ColDef 579 UpdateSqls []string 580 } 581 582 type exprType int 583 584 const ( 585 constValue exprType = iota 586 columnName 587 ) 588 589 type selectExpr struct { 590 sexprType exprType 591 sexprStr string 592 } 593 594 func initAlterTableContext(originTableDef *TableDef, copyTableDef *TableDef, schemaName string) *AlterTableContext { 595 alterTblColMap := make(map[string]selectExpr) 596 changTblColIdMap := make(map[uint64]*ColDef) 597 for _, coldef := range originTableDef.Cols { 598 if coldef.Hidden { 599 continue 600 } 601 alterTblColMap[coldef.Name] = selectExpr{ 602 sexprType: columnName, 603 sexprStr: coldef.Name, 604 } 605 606 if !coldef.Hidden { 607 changTblColIdMap[coldef.ColId] = &plan.ColDef{ 608 ColId: UnKnownColId, 609 Name: coldef.Name, 610 } 611 } 612 } 613 return &AlterTableContext{ 614 alterColMap: alterTblColMap, 615 schemaName: schemaName, 616 originTableName: originTableDef.Name, 617 copyTableName: copyTableDef.Name, 618 changColDefMap: changTblColIdMap, 619 } 620 } 621 622 func buildCopyTableDef(ctx context.Context, tableDef *TableDef) (*TableDef, error) { 623 replicaTableDef := DeepCopyTableDef(tableDef, true) 624 625 id, err := uuid.NewV7() 626 if err != nil { 627 return nil, moerr.NewInternalError(ctx, "new uuid failed") 628 } 629 replicaTableDef.Name = replicaTableDef.Name + "_copy_" + id.String() 630 return replicaTableDef, nil 631 } 632 633 func buildAlterTable(stmt *tree.AlterTable, ctx CompilerContext) (*Plan, error) { 634 // ALTER TABLE tbl_name 635 // [alter_option [, alter_option] ...] 636 // [partition_options] 637 schemaName, tableName := string(stmt.Table.Schema()), string(stmt.Table.Name()) 638 if schemaName == "" { 639 schemaName = ctx.DefaultDatabase() 640 } 641 objRef, tableDef := ctx.Resolve(schemaName, tableName, Snapshot{TS: ×tamp.Timestamp{}}) 642 if tableDef == nil { 643 return nil, moerr.NewNoSuchTable(ctx.GetContext(), schemaName, tableName) 644 } 645 646 if tableDef.IsTemporary { 647 return nil, moerr.NewNYI(ctx.GetContext(), "alter table for temporary table") 648 } 649 650 if tableDef.ViewSql != nil { 651 return nil, moerr.NewInternalError(ctx.GetContext(), "you should use alter view statemnt for View") 652 } 653 if objRef.PubInfo != nil { 654 return nil, moerr.NewInternalError(ctx.GetContext(), "cannot alter table in subscription database") 655 } 656 isClusterTable := util.TableIsClusterTable(tableDef.GetTableType()) 657 accountId, err := ctx.GetAccountId() 658 if err != nil { 659 return nil, err 660 } 661 if isClusterTable && accountId != catalog.System_Account { 662 return nil, moerr.NewInternalError(ctx.GetContext(), "only the sys account can alter the cluster table") 663 } 664 665 if tableDef.Partition != nil && stmt.Options != nil { 666 return nil, moerr.NewInvalidInput(ctx.GetContext(), "can't add/drop column for partition table now") 667 } 668 669 if stmt.PartitionOption != nil { 670 if stmt.Options != nil { 671 return nil, moerr.NewParseError(ctx.GetContext(), "Unsupported multi schema change") 672 } 673 return buildAlterTableInplace(stmt, ctx) 674 } 675 676 algorithm := ResolveAlterTableAlgorithm(ctx.GetContext(), stmt.Options) 677 if algorithm == plan.AlterTable_COPY { 678 return buildAlterTableCopy(stmt, ctx) 679 } else { 680 return buildAlterTableInplace(stmt, ctx) 681 } 682 } 683 684 func ResolveAlterTableAlgorithm(ctx context.Context, validAlterSpecs []tree.AlterTableOption) (algorithm plan.AlterTable_AlgorithmType) { 685 algorithm = plan.AlterTable_COPY 686 for _, spec := range validAlterSpecs { 687 switch option := spec.(type) { 688 case *tree.AlterOptionAdd: 689 switch option.Def.(type) { 690 case *tree.PrimaryKeyIndex: 691 algorithm = plan.AlterTable_COPY 692 case *tree.ForeignKey: 693 algorithm = plan.AlterTable_INPLACE 694 case *tree.UniqueIndex: 695 algorithm = plan.AlterTable_INPLACE 696 case *tree.Index: 697 algorithm = plan.AlterTable_INPLACE 698 case *tree.ColumnTableDef: 699 algorithm = plan.AlterTable_INPLACE 700 default: 701 algorithm = plan.AlterTable_INPLACE 702 } 703 case *tree.AlterOptionDrop: 704 switch option.Typ { 705 case tree.AlterTableDropColumn: 706 algorithm = plan.AlterTable_COPY 707 case tree.AlterTableDropIndex: 708 algorithm = plan.AlterTable_INPLACE 709 case tree.AlterTableDropKey: 710 algorithm = plan.AlterTable_INPLACE 711 case tree.AlterTableDropPrimaryKey: 712 algorithm = plan.AlterTable_COPY 713 case tree.AlterTableDropForeignKey: 714 algorithm = plan.AlterTable_INPLACE 715 default: 716 algorithm = plan.AlterTable_INPLACE 717 } 718 case *tree.AlterOptionAlterIndex: 719 algorithm = plan.AlterTable_INPLACE 720 case *tree.AlterOptionAlterReIndex: 721 algorithm = plan.AlterTable_INPLACE 722 case *tree.TableOptionComment: 723 algorithm = plan.AlterTable_INPLACE 724 case *tree.AlterOptionTableName: 725 algorithm = plan.AlterTable_INPLACE 726 case *tree.AlterAddCol: 727 algorithm = plan.AlterTable_COPY 728 case *tree.AlterTableModifyColumnClause: 729 algorithm = plan.AlterTable_COPY 730 case *tree.AlterTableChangeColumnClause: 731 algorithm = plan.AlterTable_COPY 732 case *tree.AlterTableRenameColumnClause: 733 algorithm = plan.AlterTable_COPY 734 case *tree.AlterTableAlterColumnClause: 735 algorithm = plan.AlterTable_COPY 736 case *tree.AlterTableOrderByColumnClause: 737 algorithm = plan.AlterTable_COPY 738 case *tree.TableOptionAutoIncrement: 739 algorithm = plan.AlterTable_INPLACE 740 default: 741 algorithm = plan.AlterTable_INPLACE 742 } 743 if algorithm != plan.AlterTable_COPY { 744 return algorithm 745 } 746 } 747 return algorithm 748 } 749 750 func buildNotNullColumnVal(col *ColDef) string { 751 var defaultValue string 752 if col.Typ.Id == int32(types.T_int8) || 753 col.Typ.Id == int32(types.T_int16) || 754 col.Typ.Id == int32(types.T_int32) || 755 col.Typ.Id == int32(types.T_int64) || 756 col.Typ.Id == int32(types.T_uint8) || 757 col.Typ.Id == int32(types.T_uint16) || 758 col.Typ.Id == int32(types.T_uint32) || 759 col.Typ.Id == int32(types.T_uint64) || 760 col.Typ.Id == int32(types.T_float32) || 761 col.Typ.Id == int32(types.T_float64) || 762 col.Typ.Id == int32(types.T_decimal64) || 763 col.Typ.Id == int32(types.T_decimal128) || 764 col.Typ.Id == int32(types.T_decimal256) || 765 col.Typ.Id == int32(types.T_bool) || 766 col.Typ.Id == int32(types.T_bit) { 767 defaultValue = "0" 768 } else if col.Typ.Id == int32(types.T_varchar) || 769 col.Typ.Id == int32(types.T_char) || 770 col.Typ.Id == int32(types.T_text) || 771 col.Typ.Id == int32(types.T_binary) || 772 col.Typ.Id == int32(types.T_blob) { 773 defaultValue = "''" 774 } else if col.Typ.Id == int32(types.T_date) { 775 defaultValue = "'0001-01-01'" 776 } else if col.Typ.Id == int32(types.T_datetime) { 777 defaultValue = "'0001-01-01 00:00:00'" 778 } else if col.Typ.Id == int32(types.T_time) { 779 defaultValue = "'00:00:00'" 780 } else if col.Typ.Id == int32(types.T_timestamp) { 781 defaultValue = "'0001-01-01 00:00:00'" 782 } else if col.Typ.Id == int32(types.T_json) { 783 //defaultValue = "null" 784 defaultValue = "'{}'" 785 } else if col.Typ.Id == int32(types.T_enum) { 786 enumvalues := strings.Split(col.Typ.Enumvalues, ",") 787 defaultValue = enumvalues[0] 788 } else if col.Typ.Id == int32(types.T_array_float32) || col.Typ.Id == int32(types.T_array_float64) { 789 if col.Typ.Width > 0 { 790 zerosWithCommas := strings.Repeat("0,", int(col.Typ.Width)-1) 791 arrayAsString := zerosWithCommas + "0" // final zero 792 defaultValue = fmt.Sprintf("'[%s]'", arrayAsString) 793 } else { 794 defaultValue = "'[]'" 795 } 796 } else { 797 defaultValue = "null" 798 } 799 return defaultValue 800 }