github.com/matrixorigin/matrixone@v1.2.0/pkg/sql/plan/build_alter_add_column.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 "context" 19 "fmt" 20 "math" 21 "strings" 22 23 "github.com/matrixorigin/matrixone/pkg/catalog" 24 "github.com/matrixorigin/matrixone/pkg/common/moerr" 25 "github.com/matrixorigin/matrixone/pkg/container/types" 26 "github.com/matrixorigin/matrixone/pkg/pb/plan" 27 "github.com/matrixorigin/matrixone/pkg/pb/timestamp" 28 "github.com/matrixorigin/matrixone/pkg/sql/parsers/tree" 29 "github.com/matrixorigin/matrixone/pkg/sql/util" 30 ) 31 32 // AddColumn will add a new column to the table. 33 func AddColumn(ctx CompilerContext, alterPlan *plan.AlterTable, spec *tree.AlterAddCol, alterCtx *AlterTableContext) error { 34 tableDef := alterPlan.CopyTableDef 35 36 if len(tableDef.Cols) == TableColumnCountLimit { 37 return moerr.NewErrTooManyFields(ctx.GetContext()) 38 } 39 40 specNewColumn := spec.Column 41 // Check whether added column has existed. 42 newColName := specNewColumn.Name.Parts[0] 43 if col := FindColumn(tableDef.Cols, newColName); col != nil { 44 return moerr.NewErrDupFieldName(ctx.GetContext(), newColName) 45 } 46 47 colType, err := getTypeFromAst(ctx.GetContext(), specNewColumn.Type) 48 if err != nil { 49 return err 50 } 51 if err = checkAddColumnType(ctx.GetContext(), &colType, newColName); err != nil { 52 return err 53 } 54 newCol, err := buildAddColumnAndConstraint(ctx, alterPlan, specNewColumn, colType) 55 if err != nil { 56 return err 57 } 58 if err = handleAddColumnPosition(ctx.GetContext(), tableDef, newCol, spec.Position); err != nil { 59 return err 60 } 61 62 if !newCol.Default.NullAbility && len(newCol.Default.OriginString) == 0 { 63 alterCtx.alterColMap[newCol.Name] = selectExpr{ 64 sexprType: constValue, 65 sexprStr: buildNotNullColumnVal(newCol), 66 } 67 } 68 69 return nil 70 } 71 72 // checkModifyNewColumn Check the position information of the newly formed column and place the new column in the target location 73 func handleAddColumnPosition(ctx context.Context, tableDef *TableDef, newCol *ColDef, pos *tree.ColumnPosition) error { 74 if pos != nil && pos.Typ != tree.ColumnPositionNone { 75 targetPos, err := findPositionRelativeColumn(ctx, tableDef.Cols, pos) 76 if err != nil { 77 return err 78 } 79 tableDef.Cols = append(tableDef.Cols[:targetPos], append([]*ColDef{newCol}, tableDef.Cols[targetPos:]...)...) 80 } else { 81 tableDef.Cols = append(tableDef.Cols, newCol) 82 } 83 return nil 84 } 85 86 func buildAddColumnAndConstraint(ctx CompilerContext, alterPlan *plan.AlterTable, specNewColumn *tree.ColumnTableDef, colType plan.Type) (*ColDef, error) { 87 newColName := specNewColumn.Name.Parts[0] 88 // Check if the new column name is valid and conflicts with internal hidden columns 89 err := CheckColumnNameValid(ctx.GetContext(), newColName) 90 if err != nil { 91 return nil, err 92 } 93 94 newCol := &ColDef{ 95 ColId: math.MaxUint64, 96 //Primary: originalCol.Primary, 97 //NotNull: originalCol.NotNull, 98 //Default: originalCol.Default, 99 //Comment: originalCol.Comment, 100 //OnUpdate: originalCol.OnUpdate, 101 Name: newColName, 102 Typ: colType, 103 Alg: plan.CompressType_Lz4, 104 } 105 106 hasDefaultValue := false 107 auto_incr := false 108 for _, attr := range specNewColumn.Attributes { 109 switch attribute := attr.(type) { 110 case *tree.AttributePrimaryKey, *tree.AttributeKey: 111 err = checkPrimaryKeyPartType(ctx.GetContext(), colType, newColName) 112 if err != nil { 113 return nil, err 114 } 115 // If the table already contains a primary key, an `ErrMultiplePriKey` error is reported 116 if alterPlan.CopyTableDef.Pkey != nil && alterPlan.CopyTableDef.Pkey.PkeyColName != catalog.FakePrimaryKeyColName { 117 return nil, moerr.NewErrMultiplePriKey(ctx.GetContext()) 118 } else if alterPlan.CopyTableDef.ClusterBy != nil && alterPlan.CopyTableDef.ClusterBy.Name != "" { 119 return nil, moerr.NewNotSupported(ctx.GetContext(), "cluster by with primary key is not support") 120 } else { 121 alterPlan.CopyTableDef.Pkey = &PrimaryKeyDef{ 122 Names: []string{newColName}, 123 PkeyColName: newColName, 124 } 125 newCol.Primary = true 126 } 127 case *tree.AttributeComment: 128 comment := attribute.CMT.String() 129 if getNumOfCharacters(comment) > maxLengthOfColumnComment { 130 return nil, moerr.NewInvalidInput(ctx.GetContext(), "comment for column '%s' is too long", specNewColumn.Name.Parts[0]) 131 } 132 newCol.Comment = comment 133 case *tree.AttributeAutoIncrement: 134 auto_incr = true 135 if !types.T(colType.GetId()).IsInteger() { 136 return nil, moerr.NewNotSupported(ctx.GetContext(), "the auto_incr column is only support integer type now") 137 } 138 newCol.Typ.AutoIncr = true 139 case *tree.AttributeUnique, *tree.AttributeUniqueKey: 140 err = checkUniqueKeyPartType(ctx.GetContext(), colType, newColName) 141 if err != nil { 142 return nil, err 143 } 144 uniqueIndex := &tree.UniqueIndex{ 145 KeyParts: []*tree.KeyPart{ 146 { 147 ColName: specNewColumn.Name, 148 }, 149 }, 150 } 151 152 constrNames := map[string]bool{} 153 // Check not empty constraint name whether is duplicated. 154 for _, idx := range alterPlan.CopyTableDef.Indexes { 155 nameLower := strings.ToLower(idx.IndexName) 156 constrNames[nameLower] = true 157 } 158 // set empty constraint names(index and unique index) 159 setEmptyUniqueIndexName(constrNames, uniqueIndex) 160 161 indexDef, err := checkAddColumWithUniqueKey(ctx.GetContext(), alterPlan.CopyTableDef, uniqueIndex) 162 if err != nil { 163 return nil, err 164 } 165 alterPlan.CopyTableDef.Indexes = append(alterPlan.CopyTableDef.Indexes, indexDef) 166 case *tree.AttributeDefault, *tree.AttributeNull: 167 defaultValue, err := buildDefaultExpr(specNewColumn, colType, ctx.GetProcess()) 168 if err != nil { 169 return nil, err 170 } 171 newCol.Default = defaultValue 172 hasDefaultValue = true 173 case *tree.AttributeOnUpdate: 174 onUpdateExpr, err := buildOnUpdate(specNewColumn, colType, ctx.GetProcess()) 175 if err != nil { 176 return nil, err 177 } 178 newCol.OnUpdate = onUpdateExpr 179 default: 180 return nil, moerr.NewNotSupported(ctx.GetContext(), "unsupport column definition %v", attribute) 181 } 182 } 183 if auto_incr && hasDefaultValue { 184 return nil, moerr.NewErrInvalidDefault(ctx.GetContext(), specNewColumn.Name.Parts[0]) 185 } 186 if !hasDefaultValue { 187 defaultValue, err := buildDefaultExpr(specNewColumn, colType, ctx.GetProcess()) 188 if err != nil { 189 return nil, err 190 } 191 newCol.Default = defaultValue 192 } 193 return newCol, nil 194 } 195 196 // checkAddColumnType check type for add single column. 197 func checkAddColumnType(ctx context.Context, colType *plan.Type, columnName string) error { 198 if colType.Id == int32(types.T_char) || colType.Id == int32(types.T_varchar) || 199 colType.Id == int32(types.T_binary) || colType.Id == int32(types.T_varbinary) { 200 if colType.GetWidth() > types.MaxStringSize { 201 return moerr.NewInvalidInput(ctx, "string width (%d) is too long", colType.GetWidth()) 202 } 203 } 204 205 if colType.Id == int32(types.T_array_float32) || colType.Id == int32(types.T_array_float64) { 206 if colType.GetWidth() > types.MaxArrayDimension { 207 return moerr.NewInvalidInput(ctx, "vector width (%d) is too long", colType.GetWidth()) 208 } 209 } 210 return nil 211 } 212 213 func checkPrimaryKeyPartType(ctx context.Context, colType plan.Type, columnName string) error { 214 if colType.GetId() == int32(types.T_blob) { 215 return moerr.NewNotSupported(ctx, "blob type in primary key") 216 } 217 if colType.GetId() == int32(types.T_text) { 218 return moerr.NewNotSupported(ctx, "text type in primary key") 219 } 220 if colType.GetId() == int32(types.T_json) { 221 return moerr.NewNotSupported(ctx, fmt.Sprintf("JSON column '%s' cannot be in primary key", columnName)) 222 } 223 if colType.GetId() == int32(types.T_enum) { 224 return moerr.NewNotSupported(ctx, fmt.Sprintf("ENUM column '%s' cannot be in primary key", columnName)) 225 } 226 return nil 227 } 228 229 func checkUniqueKeyPartType(ctx context.Context, colType plan.Type, columnName string) error { 230 if colType.GetId() == int32(types.T_blob) { 231 return moerr.NewNotSupported(ctx, "blob type in primary key") 232 } 233 if colType.GetId() == int32(types.T_text) { 234 return moerr.NewNotSupported(ctx, "text type in primary key") 235 } 236 if colType.GetId() == int32(types.T_json) { 237 return moerr.NewNotSupported(ctx, fmt.Sprintf("JSON column '%s' cannot be in primary key", columnName)) 238 } 239 return nil 240 } 241 242 func checkAddColumWithUniqueKey(ctx context.Context, tableDef *TableDef, uniKey *tree.UniqueIndex) (*plan.IndexDef, error) { 243 indexName := uniKey.GetIndexName() 244 if strings.EqualFold(indexName, PrimaryKeyName) { 245 return nil, moerr.NewErrWrongNameForIndex(ctx, uniKey.GetIndexName()) 246 } 247 248 indexTableName, err := util.BuildIndexTableName(ctx, true) 249 if err != nil { 250 return nil, err 251 } 252 253 indexParts := make([]string, 0) 254 for _, keyPart := range uniKey.KeyParts { 255 name := keyPart.ColName.Parts[0] 256 indexParts = append(indexParts, name) 257 } 258 if len(indexParts) > MaxKeyParts { 259 return nil, moerr.NewErrTooManyKeyParts(ctx, MaxKeyParts) 260 } 261 262 indexDef := &plan.IndexDef{ 263 IndexName: indexName, 264 Unique: true, 265 Parts: indexParts, 266 IndexTableName: indexTableName, 267 TableExist: true, 268 Comment: "", 269 } 270 271 if uniKey.IndexOption != nil { 272 indexDef.Comment = uniKey.IndexOption.Comment 273 } 274 return indexDef, nil 275 } 276 277 // findPositionRelativeColumn returns a position relative to the position of the add/modify/change column. 278 func findPositionRelativeColumn(ctx context.Context, cols []*ColDef, pos *tree.ColumnPosition) (int, error) { 279 position := len(cols) 280 // gets the position of the column, which defaults to the length of the column indicating appending. 281 if pos == nil { 282 return position, nil 283 } 284 if pos.Typ == tree.ColumnPositionFirst { 285 position = 0 286 } else if pos.Typ == tree.ColumnPositionAfter { 287 relcolIndex := -1 288 for i, col := range cols { 289 if col.Name == pos.RelativeColumn.Parts[0] { 290 relcolIndex = i 291 break 292 } 293 } 294 if relcolIndex == -1 { 295 return -1, moerr.NewBadFieldError(ctx, pos.RelativeColumn.Parts[0], "Columns Set") 296 } 297 // the insertion position is after the above column. 298 position = int(relcolIndex + 1) 299 } 300 return position, nil 301 } 302 303 // AddColumn will add a new column to the table. 304 func DropColumn(ctx CompilerContext, alterPlan *plan.AlterTable, colName string, alterCtx *AlterTableContext) error { 305 tableDef := alterPlan.CopyTableDef 306 // Check whether original column has existed. 307 col := FindColumn(tableDef.Cols, colName) 308 if col == nil || col.Hidden { 309 return moerr.NewErrCantDropFieldOrKey(ctx.GetContext(), colName) 310 } 311 312 // We only support dropping column with single-value none Primary Key index covered now. 313 if err := handleDropColumnWithIndex(ctx.GetContext(), colName, tableDef); err != nil { 314 return err 315 } 316 if err := handleDropColumnWithPrimaryKey(ctx.GetContext(), colName, tableDef); err != nil { 317 return err 318 } 319 if err := checkDropColumnWithPartition(ctx.GetContext(), tableDef, colName); err != nil { 320 return err 321 } 322 // Check the column with foreign key. 323 if err := checkDropColumnWithForeignKey(ctx, tableDef, col); err != nil { 324 return err 325 } 326 if err := checkVisibleColumnCnt(ctx.GetContext(), tableDef, 0, 1); err != nil { 327 return err 328 } 329 if isColumnWithPartition(col.Name, tableDef.Partition) { 330 return moerr.NewNotSupported(ctx.GetContext(), "unsupport alter partition part column currently") 331 } 332 333 if err := handleDropColumnPosition(ctx.GetContext(), tableDef, col); err != nil { 334 return err 335 } 336 337 if err := handleDropColumnWithClusterBy(ctx.GetContext(), tableDef, col); err != nil { 338 return err 339 } 340 341 delete(alterCtx.alterColMap, colName) 342 return nil 343 } 344 345 func checkVisibleColumnCnt(ctx context.Context, tblInfo *TableDef, addCount, dropCount int) error { 346 visibleColumCnt := 0 347 for _, column := range tblInfo.Cols { 348 if !column.Hidden { 349 visibleColumCnt++ 350 } 351 } 352 if visibleColumCnt+addCount > dropCount { 353 return nil 354 } 355 if len(tblInfo.Cols)-visibleColumCnt > 0 { 356 // There are only invisible columns. 357 return moerr.NewErrTableMustHaveColumns(ctx) 358 } 359 return moerr.NewErrCantRemoveAllFields(ctx) 360 } 361 362 func handleDropColumnWithIndex(ctx context.Context, colName string, tbInfo *TableDef) error { 363 for i := 0; i < len(tbInfo.Indexes); i++ { 364 indexInfo := tbInfo.Indexes[i] 365 for j := 0; j < len(indexInfo.Parts); j++ { 366 if catalog.ResolveAlias(indexInfo.Parts[j]) == colName { 367 indexInfo.Parts = append(indexInfo.Parts[:j], indexInfo.Parts[j+1:]...) 368 break 369 } 370 } 371 if indexInfo.Unique { 372 // handle unique index 373 if len(indexInfo.Parts) == 0 { 374 tbInfo.Indexes = append(tbInfo.Indexes[:i], tbInfo.Indexes[i+1:]...) 375 } 376 } else if !indexInfo.Unique { 377 // handle secondary index 378 switch catalog.ToLower(indexInfo.IndexAlgo) { 379 case catalog.MoIndexDefaultAlgo.ToString(), catalog.MoIndexBTreeAlgo.ToString(): 380 // regular secondary index 381 if len(indexInfo.Parts) == 1 && 382 (catalog.IsAlias(indexInfo.Parts[0]) || 383 indexInfo.Parts[0] == catalog.FakePrimaryKeyColName || 384 indexInfo.Parts[0] == catalog.CPrimaryKeyColName) { 385 // Handles deleting the secondary index when there is no more user defined secondary keys. 386 387 //NOTE: if the last SK column is an __mo_alias or __mo_fake or __mo_cp, then the index will be deleted. 388 // There is no way that user can add __mo_alias or __mo_fake or __mo_cp as the SK column. 389 tbInfo.Indexes = append(tbInfo.Indexes[:i], tbInfo.Indexes[i+1:]...) 390 } else if len(indexInfo.Parts) == 0 { 391 tbInfo.Indexes = append(tbInfo.Indexes[:i], tbInfo.Indexes[i+1:]...) 392 } 393 case catalog.MoIndexIvfFlatAlgo.ToString(): 394 // ivf index 395 if len(indexInfo.Parts) == 0 { 396 // remove 3 index records: metadata, centroids, entries 397 tbInfo.Indexes = append(tbInfo.Indexes[:i], tbInfo.Indexes[i+3:]...) 398 } 399 case catalog.MOIndexMasterAlgo.ToString(): 400 if len(indexInfo.Parts) == 0 { 401 // TODO: verify this 402 tbInfo.Indexes = append(tbInfo.Indexes[:i], tbInfo.Indexes[i+1:]...) 403 } 404 } 405 } 406 } 407 return nil 408 } 409 410 func handleDropColumnWithPrimaryKey(ctx context.Context, colName string, tbInfo *TableDef) error { 411 if tbInfo.Pkey != nil && tbInfo.Pkey.PkeyColName == catalog.FakePrimaryKeyColName { 412 return nil 413 } else { 414 for i := 0; i < len(tbInfo.Pkey.Names); i++ { 415 if tbInfo.Pkey.Names[i] == colName { 416 tbInfo.Pkey.Names = append(tbInfo.Pkey.Names[:i], tbInfo.Pkey.Names[i+1:]...) 417 break 418 } 419 } 420 421 if len(tbInfo.Pkey.Names) == 0 { 422 tbInfo.Pkey = nil 423 } else if len(tbInfo.Pkey.Names) == 1 { 424 tbInfo.Pkey.PkeyColName = tbInfo.Pkey.Names[0] 425 for _, coldef := range tbInfo.Cols { 426 if coldef.Name == tbInfo.Pkey.PkeyColName { 427 coldef.Primary = true 428 break 429 } 430 } 431 } 432 433 return nil 434 } 435 } 436 437 func checkDropColumnWithForeignKey(ctx CompilerContext, tbInfo *TableDef, targetCol *ColDef) error { 438 colName := targetCol.Name 439 for _, fkInfo := range tbInfo.Fkeys { 440 for _, colId := range fkInfo.Cols { 441 referCol := FindColumnByColId(tbInfo.Cols, colId) 442 if referCol == nil { 443 continue 444 } 445 if referCol.Name == colName { 446 return moerr.NewErrFkColumnCannotDrop(ctx.GetContext(), colName, fkInfo.Name) 447 } 448 } 449 } 450 451 for _, referredTblId := range tbInfo.RefChildTbls { 452 _, refTableDef := ctx.ResolveById(referredTblId, Snapshot{TS: ×tamp.Timestamp{}}) 453 if refTableDef == nil { 454 return moerr.NewInternalError(ctx.GetContext(), "The reference foreign key table %d does not exist", referredTblId) 455 } 456 for _, referredFK := range refTableDef.Fkeys { 457 if referredFK.ForeignTbl == tbInfo.TblId { 458 for i := 0; i < len(referredFK.Cols); i++ { 459 if referredFK.ForeignCols[i] == targetCol.ColId { 460 return moerr.NewErrFkColumnCannotDropChild(ctx.GetContext(), colName, referredFK.Name, refTableDef.Name) 461 } 462 } 463 } 464 } 465 } 466 return nil 467 } 468 469 // checkDropColumnWithPartition is used to check the partition key of the drop column. 470 func checkDropColumnWithPartition(ctx context.Context, tbInfo *TableDef, colName string) error { 471 if tbInfo.Partition != nil { 472 partition := tbInfo.Partition 473 // TODO Implement this method in the future to obtain the partition column in the partition expression 474 // func (m *PartitionByDef) GetPartitionColumnNames() []string 475 for _, name := range partition.GetPartitionColumns().PartitionColumns { 476 if strings.EqualFold(name, colName) { 477 return moerr.NewErrDependentByPartitionFunction(ctx, colName) 478 } 479 } 480 } 481 return nil 482 } 483 484 // checkModifyNewColumn Check the position information of the newly formed column and place the new column in the target location 485 func handleDropColumnPosition(ctx context.Context, tableDef *TableDef, col *ColDef) error { 486 targetPos := -1 487 for i := 0; i < len(tableDef.Cols); i++ { 488 if tableDef.Cols[i].Name == col.Name { 489 targetPos = i 490 break 491 } 492 } 493 tableDef.Cols = append(tableDef.Cols[:targetPos], tableDef.Cols[targetPos+1:]...) 494 return nil 495 } 496 497 // handleDropColumnWithClusterBy Process the cluster by table. If the cluster by key name is deleted, proceed with the process 498 func handleDropColumnWithClusterBy(ctx context.Context, copyTableDef *TableDef, originCol *ColDef) error { 499 if copyTableDef.ClusterBy != nil && copyTableDef.ClusterBy.Name != "" { 500 clusterBy := copyTableDef.ClusterBy 501 var clNames []string 502 if util.JudgeIsCompositeClusterByColumn(clusterBy.Name) { 503 clNames = util.SplitCompositeClusterByColumnName(clusterBy.Name) 504 } else { 505 clNames = []string{clusterBy.Name} 506 } 507 deleteIndex := -1 508 for j, part := range clNames { 509 if part == originCol.Name { 510 deleteIndex = j 511 break 512 } 513 } 514 515 if deleteIndex != -1 { 516 clNames = append(clNames[:deleteIndex], clNames[deleteIndex+1:]...) 517 } 518 519 if len(clNames) == 0 { 520 copyTableDef.ClusterBy = nil 521 } else if len(clNames) == 1 { 522 copyTableDef.ClusterBy = &plan.ClusterByDef{ 523 Name: clNames[0], 524 } 525 } else { 526 clusterByColName := util.BuildCompositeClusterByColumnName(clNames) 527 copyTableDef.ClusterBy = &plan.ClusterByDef{ 528 Name: clusterByColName, 529 } 530 } 531 } 532 return nil 533 }