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  }