github.com/matrixorigin/matrixone@v1.2.0/pkg/sql/plan/build_alter_change_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 "github.com/matrixorigin/matrixone/pkg/catalog" 20 "github.com/matrixorigin/matrixone/pkg/common/moerr" 21 "github.com/matrixorigin/matrixone/pkg/container/types" 22 "github.com/matrixorigin/matrixone/pkg/pb/plan" 23 "github.com/matrixorigin/matrixone/pkg/sql/parsers/tree" 24 "github.com/matrixorigin/matrixone/pkg/sql/util" 25 "strings" 26 ) 27 28 // ChangeColumn Can rename a column and change its definition, or both. Has more capability than MODIFY or RENAME COLUMN, 29 // but at the expense of convenience for some operations. CHANGE requires naming the column twice if not renaming it, and 30 // requires respecifying the column definition if only renaming it. With FIRST or AFTER, can reorder columns. 31 func ChangeColumn(ctx CompilerContext, alterPlan *plan.AlterTable, spec *tree.AlterTableChangeColumnClause, alterCtx *AlterTableContext) error { 32 tableDef := alterPlan.CopyTableDef 33 34 // get the original column name 35 originalColName := spec.OldColumnName.Parts[0] 36 37 specNewColumn := spec.NewColumn 38 // get the new column name 39 newColName := specNewColumn.Name.Parts[0] 40 41 // Check whether original column has existed. 42 col := FindColumn(tableDef.Cols, originalColName) 43 if col == nil || col.Hidden { 44 return moerr.NewBadFieldError(ctx.GetContext(), originalColName, alterPlan.TableDef.Name) 45 } 46 47 if isColumnWithPartition(col.Name, tableDef.Partition) { 48 return moerr.NewNotSupported(ctx.GetContext(), "unsupport alter partition part column currently") 49 } 50 51 // If you want to rename the original column name to new name, you need to first check if the new name already exists. 52 if newColName != originalColName { 53 newcol := FindColumn(tableDef.Cols, newColName) 54 if newcol != nil { 55 return moerr.NewErrDupFieldName(ctx.GetContext(), newColName) 56 } 57 58 //change the name of the column in the foreign key constraint 59 alterCtx.UpdateSqls = append(alterCtx.UpdateSqls, 60 getSqlForRenameColumn(alterPlan.Database, 61 alterPlan.TableDef.Name, 62 originalColName, 63 newColName)...) 64 } 65 66 colType, err := getTypeFromAst(ctx.GetContext(), specNewColumn.Type) 67 if err != nil { 68 return err 69 } 70 71 // check if the newly added column type is valid 72 if err = checkAddColumnType(ctx.GetContext(), &colType, newColName); err != nil { 73 return err 74 } 75 76 newCol, err := buildChangeColumnAndConstraint(ctx, alterPlan, col, specNewColumn, colType) 77 if err != nil { 78 return err 79 } 80 81 // check new column foreign key constraints 82 if err = CheckModifyColumnForeignkeyConstraint(ctx, tableDef, col, newCol); err != nil { 83 return err 84 } 85 86 if err = checkChangeTypeCompatible(ctx.GetContext(), &col.Typ, &newCol.Typ); err != nil { 87 return err 88 } 89 90 if err = checkModifyNewColumn(ctx.GetContext(), tableDef, col, newCol, spec.Position); err != nil { 91 return err 92 } 93 94 handleClusterByKey(ctx.GetContext(), alterPlan, newColName, originalColName) 95 96 delete(alterCtx.alterColMap, col.Name) 97 alterCtx.alterColMap[newCol.Name] = selectExpr{ 98 sexprType: columnName, 99 sexprStr: col.Name, 100 } 101 102 if tmpCol, ok := alterCtx.changColDefMap[col.ColId]; ok { 103 tmpCol.Name = newCol.Name 104 } 105 return nil 106 } 107 108 // buildChangeColumnAndConstraint Build the changed new column definition, and check its column level integrity constraints, 109 // and check other table level constraints, such as primary keys, indexes, etc 110 func buildChangeColumnAndConstraint(ctx CompilerContext, alterPlan *plan.AlterTable, originalCol *ColDef, specNewColumn *tree.ColumnTableDef, colType plan.Type) (*ColDef, error) { 111 newColName := specNewColumn.Name.Parts[0] 112 // Check if the new column name is valid and conflicts with internal hidden columns 113 err := CheckColumnNameValid(ctx.GetContext(), newColName) 114 if err != nil { 115 return nil, err 116 } 117 118 newCol := &ColDef{ 119 ColId: originalCol.ColId, 120 Primary: originalCol.Primary, 121 ClusterBy: originalCol.ClusterBy, 122 Name: newColName, 123 Typ: colType, 124 Alg: plan.CompressType_Lz4, 125 } 126 127 hasDefaultValue := false 128 hasNullFlag := false 129 auto_incr := false 130 for _, attr := range specNewColumn.Attributes { 131 switch attribute := attr.(type) { 132 case *tree.AttributePrimaryKey, *tree.AttributeKey: 133 err = checkPrimaryKeyPartType(ctx.GetContext(), colType, newColName) 134 if err != nil { 135 return nil, err 136 } 137 // If the table already contains a primary key, an `ErrMultiplePriKey` error is reported 138 if alterPlan.CopyTableDef.Pkey != nil && alterPlan.CopyTableDef.Pkey.PkeyColName != catalog.FakePrimaryKeyColName { 139 return nil, moerr.NewErrMultiplePriKey(ctx.GetContext()) 140 } else if alterPlan.CopyTableDef.ClusterBy != nil && alterPlan.CopyTableDef.ClusterBy.Name != "" { 141 return nil, moerr.NewNotSupported(ctx.GetContext(), "cluster by with primary key is not support") 142 } else { 143 alterPlan.CopyTableDef.Pkey = &PrimaryKeyDef{ 144 Names: []string{newColName}, 145 PkeyColName: newColName, 146 } 147 newCol.Primary = true 148 } 149 case *tree.AttributeComment: 150 comment := attribute.CMT.String() 151 if getNumOfCharacters(comment) > maxLengthOfColumnComment { 152 return nil, moerr.NewInvalidInput(ctx.GetContext(), "comment for column '%s' is too long", specNewColumn.Name.Parts[0]) 153 } 154 newCol.Comment = comment 155 case *tree.AttributeAutoIncrement: 156 auto_incr = true 157 if !types.T(colType.GetId()).IsInteger() { 158 return nil, moerr.NewNotSupported(ctx.GetContext(), "the auto_incr column is only support integer type now") 159 } 160 newCol.Typ.AutoIncr = true 161 case *tree.AttributeUnique, *tree.AttributeUniqueKey: 162 err = checkUniqueKeyPartType(ctx.GetContext(), colType, newColName) 163 if err != nil { 164 return nil, err 165 } 166 uniqueIndex := &tree.UniqueIndex{ 167 KeyParts: []*tree.KeyPart{ 168 { 169 ColName: specNewColumn.Name, 170 }, 171 }, 172 } 173 174 constrNames := map[string]bool{} 175 // Check not empty constraint name whether is duplicated. 176 for _, idx := range alterPlan.CopyTableDef.Indexes { 177 nameLower := strings.ToLower(idx.IndexName) 178 constrNames[nameLower] = true 179 } 180 // set empty constraint names(index and unique index) 181 setEmptyUniqueIndexName(constrNames, uniqueIndex) 182 183 indexDef, err := checkAddColumWithUniqueKey(ctx.GetContext(), alterPlan.CopyTableDef, uniqueIndex) 184 if err != nil { 185 return nil, err 186 } 187 alterPlan.CopyTableDef.Indexes = append(alterPlan.CopyTableDef.Indexes, indexDef) 188 case *tree.AttributeDefault: 189 defaultValue, err := buildDefaultExpr(specNewColumn, colType, ctx.GetProcess()) 190 if err != nil { 191 return nil, err 192 } 193 newCol.Default = defaultValue 194 hasDefaultValue = true 195 case *tree.AttributeNull: 196 defaultValue, err := buildDefaultExpr(specNewColumn, colType, ctx.GetProcess()) 197 if err != nil { 198 return nil, err 199 } 200 newCol.Default = defaultValue 201 hasNullFlag = true 202 case *tree.AttributeOnUpdate: 203 onUpdateExpr, err := buildOnUpdate(specNewColumn, colType, ctx.GetProcess()) 204 if err != nil { 205 return nil, err 206 } 207 newCol.OnUpdate = onUpdateExpr 208 default: 209 return nil, moerr.NewNotSupported(ctx.GetContext(), "unsupport column definition %v", attribute) 210 } 211 } 212 if auto_incr && hasDefaultValue { 213 return nil, moerr.NewErrInvalidDefault(ctx.GetContext(), specNewColumn.Name.Parts[0]) 214 } 215 if !hasDefaultValue { 216 defaultValue, err := buildDefaultExpr(specNewColumn, colType, ctx.GetProcess()) 217 if err != nil { 218 return nil, err 219 } 220 newCol.Default = defaultValue 221 } 222 223 // If the column name of the table changes, it is necessary to check if it is associated 224 // with the index key. If it is an index key column, column name replacement is required. 225 if newColName != originalCol.Name { 226 for _, indexInfo := range alterPlan.CopyTableDef.Indexes { 227 for j, partCol := range indexInfo.Parts { 228 partCol = catalog.ResolveAlias(partCol) 229 if partCol == originalCol.Name { 230 indexInfo.Parts[j] = newColName 231 } 232 } 233 } 234 235 primaryKeyDef := alterPlan.CopyTableDef.Pkey 236 for j, partCol := range primaryKeyDef.Names { 237 if partCol == originalCol.Name { 238 primaryKeyDef.Names[j] = newColName 239 break 240 } 241 } 242 } 243 244 if alterPlan.CopyTableDef.Pkey != nil { 245 for _, partCol := range alterPlan.CopyTableDef.Pkey.Names { 246 if partCol == newCol.Name { 247 newCol.Default.NullAbility = false 248 newCol.NotNull = true 249 break 250 } 251 } 252 } 253 254 if err = checkPriKeyConstraint(ctx.GetContext(), newCol, hasDefaultValue, hasNullFlag, alterPlan.CopyTableDef.Pkey); err != nil { 255 return nil, err 256 } 257 258 return newCol, nil 259 } 260 261 // Check if the column name is valid and conflicts with internal hidden columns 262 func CheckColumnNameValid(ctx context.Context, colName string) error { 263 if _, ok := catalog.InternalColumns[colName]; ok { 264 return moerr.NewErrWrongColumnName(ctx, colName) 265 } 266 return nil 267 } 268 269 // handleClusterByKey Process the cluster by table. If the cluster by key name is modified, proceed with the process 270 func handleClusterByKey(ctx context.Context, alterPlan *plan.AlterTable, newColName string, originalColName string) error { 271 if alterPlan.CopyTableDef.ClusterBy != nil && alterPlan.CopyTableDef.ClusterBy.Name != "" { 272 clusterBy := alterPlan.CopyTableDef.ClusterBy 273 var clNames []string 274 if util.JudgeIsCompositeClusterByColumn(clusterBy.Name) { 275 clNames = util.SplitCompositeClusterByColumnName(clusterBy.Name) 276 } else { 277 clNames = []string{clusterBy.Name} 278 } 279 for j, part := range clNames { 280 if part == originalColName { 281 clNames[j] = newColName 282 break 283 } 284 } 285 286 if len(clNames) == 1 { 287 alterPlan.CopyTableDef.ClusterBy = &plan.ClusterByDef{ 288 Name: clNames[0], 289 } 290 } else { 291 clusterByColName := util.BuildCompositeClusterByColumnName(clNames) 292 alterPlan.CopyTableDef.ClusterBy = &plan.ClusterByDef{ 293 Name: clusterByColName, 294 } 295 } 296 } 297 return nil 298 }