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