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: &timestamp.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  }