github.com/matrixorigin/matrixone@v1.2.0/pkg/sql/compile/alter.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 compile
    16  
    17  import (
    18  	"fmt"
    19  
    20  	plan2 "github.com/matrixorigin/matrixone/pkg/sql/plan"
    21  	"go.uber.org/zap"
    22  
    23  	"github.com/matrixorigin/matrixone/pkg/catalog"
    24  	"github.com/matrixorigin/matrixone/pkg/common/moerr"
    25  	"github.com/matrixorigin/matrixone/pkg/pb/api"
    26  	"github.com/matrixorigin/matrixone/pkg/pb/lock"
    27  	"github.com/matrixorigin/matrixone/pkg/pb/plan"
    28  	"github.com/matrixorigin/matrixone/pkg/vm/engine"
    29  )
    30  
    31  func (s *Scope) AlterTableCopy(c *Compile) error {
    32  	qry := s.Plan.GetDdl().GetAlterTable()
    33  	dbName := qry.Database
    34  	if dbName == "" {
    35  		dbName = c.db
    36  	}
    37  	tblName := qry.GetTableDef().GetName()
    38  
    39  	dbSource, err := c.e.Database(c.ctx, dbName, c.proc.TxnOperator)
    40  	if err != nil {
    41  		return err
    42  	}
    43  
    44  	originRel, err := dbSource.Relation(c.ctx, tblName, nil)
    45  	if err != nil {
    46  		return err
    47  	}
    48  
    49  	if c.proc.TxnOperator.Txn().IsPessimistic() {
    50  		var retryErr error
    51  		// 1. lock origin table metadata in catalog
    52  		if err = lockMoTable(c, dbName, tblName, lock.LockMode_Exclusive); err != nil {
    53  			if !moerr.IsMoErrCode(err, moerr.ErrTxnNeedRetry) &&
    54  				!moerr.IsMoErrCode(err, moerr.ErrTxnNeedRetryWithDefChanged) {
    55  				return err
    56  			}
    57  			retryErr = err
    58  		}
    59  
    60  		// 2. lock origin table
    61  		var partitionTableNames []string
    62  		tableDef := qry.GetTableDef()
    63  		if tableDef.Partition != nil {
    64  			partitionTableNames = tableDef.Partition.PartitionTableNames
    65  		}
    66  		if err = lockTable(c.ctx, c.e, c.proc, originRel, dbName, partitionTableNames, true); err != nil {
    67  			if !moerr.IsMoErrCode(err, moerr.ErrTxnNeedRetry) &&
    68  				!moerr.IsMoErrCode(err, moerr.ErrTxnNeedRetryWithDefChanged) {
    69  				return err
    70  			}
    71  			retryErr = err
    72  		}
    73  		if retryErr != nil {
    74  			return retryErr
    75  		}
    76  	}
    77  
    78  	// 3. create temporary replica table which doesn't have foreign key constraints
    79  	err = c.runSql(qry.CreateTmpTableSql)
    80  	if err != nil {
    81  		getLogger().Info("Create copy table for alter table",
    82  			zap.String("databaseName", c.db),
    83  			zap.String("origin tableName", qry.GetTableDef().Name),
    84  			zap.String("copy tableName", qry.CopyTableDef.Name),
    85  			zap.String("CreateTmpTableSql", qry.CreateTmpTableSql),
    86  			zap.Error(err))
    87  		return err
    88  	}
    89  
    90  	// 4. copy the original table data to the temporary replica table
    91  	err = c.runSql(qry.InsertTmpDataSql)
    92  	if err != nil {
    93  		getLogger().Info("insert data to copy table for alter table",
    94  			zap.String("databaseName", c.db),
    95  			zap.String("origin tableName", qry.GetTableDef().Name),
    96  			zap.String("copy tableName", qry.CopyTableDef.Name),
    97  			zap.String("InsertTmpDataSql", qry.InsertTmpDataSql),
    98  			zap.Error(err))
    99  		return err
   100  	}
   101  
   102  	// 5. drop original table
   103  	if err = dbSource.Delete(c.ctx, tblName); err != nil {
   104  		getLogger().Info("drop original table for alter table",
   105  			zap.String("databaseName", c.db),
   106  			zap.String("origin tableName", qry.GetTableDef().Name),
   107  			zap.String("copy tableName", qry.CopyTableDef.Name),
   108  			zap.Error(err))
   109  		return err
   110  	}
   111  
   112  	// 5.1 delete all index objects of the table in mo_catalog.mo_indexes
   113  	if qry.Database != catalog.MO_CATALOG && qry.TableDef.Name != catalog.MO_INDEXES {
   114  		if qry.GetTableDef().Pkey != nil || len(qry.GetTableDef().Indexes) > 0 {
   115  			deleteSql := fmt.Sprintf(deleteMoIndexesWithTableIdFormat, qry.GetTableDef().TblId)
   116  			err = c.runSql(deleteSql)
   117  			if err != nil {
   118  				getLogger().Info("delete all index meta data of origin table in `mo_indexes` for alter table",
   119  					zap.String("databaseName", c.db),
   120  					zap.String("origin tableName", qry.GetTableDef().Name),
   121  					zap.String("delete all index sql", deleteSql),
   122  					zap.Error(err))
   123  
   124  				return err
   125  			}
   126  		}
   127  	}
   128  
   129  	// 5.2 delete all index table of the original table
   130  	if qry.TableDef.Indexes != nil {
   131  		for _, indexdef := range qry.TableDef.Indexes {
   132  			if indexdef.TableExist {
   133  				if err = dbSource.Delete(c.ctx, indexdef.IndexTableName); err != nil {
   134  					getLogger().Info("delete all index table of origin table for alter table",
   135  						zap.String("databaseName", c.db),
   136  						zap.String("origin tableName", qry.GetTableDef().Name),
   137  						zap.String("origin tableName index table", indexdef.IndexTableName),
   138  						zap.Error(err))
   139  					return err
   140  				}
   141  			}
   142  		}
   143  	}
   144  
   145  	//6. obtain relation for new tables
   146  	newRel, err := dbSource.Relation(c.ctx, qry.CopyTableDef.Name, nil)
   147  	if err != nil {
   148  		getLogger().Info("obtain new relation for copy table for alter table",
   149  			zap.String("databaseName", c.db),
   150  			zap.String("origin tableName", qry.GetTableDef().Name),
   151  			zap.String("copy table name", qry.CopyTableDef.Name),
   152  			zap.Error(err))
   153  		return err
   154  	}
   155  
   156  	//--------------------------------------------------------------------------------------------------------------
   157  	// 7. rename temporary replica table into the original table( Table Id remains unchanged)
   158  	copyTblName := qry.CopyTableDef.Name
   159  	req := api.NewRenameTableReq(newRel.GetDBID(c.ctx), newRel.GetTableID(c.ctx), copyTblName, tblName)
   160  	tmp, err := req.Marshal()
   161  	if err != nil {
   162  		return err
   163  	}
   164  	constraint := make([][]byte, 0)
   165  	constraint = append(constraint, tmp)
   166  	err = newRel.TableRenameInTxn(c.ctx, constraint)
   167  	if err != nil {
   168  		getLogger().Info("Rename copy tableName to origin tableName in for alter table",
   169  			zap.String("origin tableName", qry.GetTableDef().Name),
   170  			zap.String("copy table name", qry.CopyTableDef.Name),
   171  			zap.Error(err))
   172  		return err
   173  	}
   174  	//--------------------------------------------------------------------------------------------------------------
   175  	{
   176  		// 8. invoke reindex for the new table, if it contains ivf index.
   177  		multiTableIndexes := make(map[string]*MultiTableIndex)
   178  		newTableDef := newRel.CopyTableDef(c.ctx)
   179  
   180  		for _, indexDef := range newTableDef.Indexes {
   181  			if catalog.IsIvfIndexAlgo(indexDef.IndexAlgo) {
   182  				if _, ok := multiTableIndexes[indexDef.IndexName]; !ok {
   183  					multiTableIndexes[indexDef.IndexName] = &MultiTableIndex{
   184  						IndexAlgo: catalog.ToLower(indexDef.IndexAlgo),
   185  						IndexDefs: make(map[string]*plan.IndexDef),
   186  					}
   187  				}
   188  				multiTableIndexes[indexDef.IndexName].IndexDefs[catalog.ToLower(indexDef.IndexAlgoTableType)] = indexDef
   189  			}
   190  		}
   191  		for _, multiTableIndex := range multiTableIndexes {
   192  			switch multiTableIndex.IndexAlgo {
   193  			case catalog.MoIndexIvfFlatAlgo.ToString():
   194  				err = s.handleVectorIvfFlatIndex(c, multiTableIndex.IndexDefs, qry.Database, newTableDef, nil)
   195  			}
   196  			if err != nil {
   197  				getLogger().Info("invoke reindex for the new table for alter table",
   198  					zap.String("origin tableName", qry.GetTableDef().Name),
   199  					zap.String("copy table name", qry.CopyTableDef.Name),
   200  					zap.String("indexAlgo", multiTableIndex.IndexAlgo),
   201  					zap.Error(err))
   202  				return err
   203  			}
   204  		}
   205  	}
   206  
   207  	// get and update the change mapping information of table colIds
   208  	if err = updateNewTableColId(c, newRel, qry.ChangeTblColIdMap); err != nil {
   209  		getLogger().Info("get and update the change mapping information of table colIds for alter table",
   210  			zap.String("origin tableName", qry.GetTableDef().Name),
   211  			zap.String("copy table name", qry.CopyTableDef.Name),
   212  			zap.Error(err))
   213  		return err
   214  	}
   215  
   216  	if len(qry.CopyTableDef.RefChildTbls) > 0 {
   217  		// Restore the original table's foreign key child table ids to the copy table definition
   218  		if err = restoreNewTableRefChildTbls(c, newRel, qry.CopyTableDef.RefChildTbls); err != nil {
   219  			getLogger().Info("Restore original table's foreign key child table ids to copyTable definition for alter table",
   220  				zap.String("origin tableName", qry.GetTableDef().Name),
   221  				zap.String("copy table name", qry.CopyTableDef.Name),
   222  				zap.Error(err))
   223  			return err
   224  		}
   225  
   226  		// update foreign key child table references to the current table
   227  		for _, tblId := range qry.CopyTableDef.RefChildTbls {
   228  			if err = updateTableForeignKeyColId(c, qry.ChangeTblColIdMap, tblId, originRel.GetTableID(c.ctx), newRel.GetTableID(c.ctx)); err != nil {
   229  				getLogger().Info("update foreign key child table references to the current table for alter table",
   230  					zap.String("origin tableName", qry.GetTableDef().Name),
   231  					zap.String("copy table name", qry.CopyTableDef.Name),
   232  					zap.Error(err))
   233  				return err
   234  			}
   235  		}
   236  	}
   237  
   238  	if len(qry.TableDef.Fkeys) > 0 {
   239  		for _, fkey := range qry.CopyTableDef.Fkeys {
   240  			if err = notifyParentTableFkTableIdChange(c, fkey, originRel.GetTableID(c.ctx)); err != nil {
   241  				getLogger().Info("notify parent table foreign key TableId Change for alter table",
   242  					zap.String("origin tableName", qry.GetTableDef().Name),
   243  					zap.String("copy table name", qry.CopyTableDef.Name),
   244  					zap.Error(err))
   245  				return err
   246  			}
   247  		}
   248  	}
   249  	return nil
   250  }
   251  
   252  func (s *Scope) AlterTable(c *Compile) (err error) {
   253  	qry := s.Plan.GetDdl().GetAlterTable()
   254  	if qry.AlgorithmType == plan.AlterTable_COPY {
   255  		err = s.AlterTableCopy(c)
   256  	} else {
   257  		err = s.AlterTableInplace(c)
   258  	}
   259  	if err != nil {
   260  		return err
   261  	}
   262  
   263  	if !plan2.IsFkBannedDatabase(qry.Database) {
   264  		//update the mo_foreign_keys
   265  		for _, sql := range qry.UpdateFkSqls {
   266  			err = c.runSql(sql)
   267  			if err != nil {
   268  				return err
   269  			}
   270  		}
   271  	}
   272  
   273  	return err
   274  }
   275  
   276  // updateTableForeignKeyColId update foreign key colid of child table references
   277  func updateTableForeignKeyColId(c *Compile, changColDefMap map[uint64]*plan.ColDef, childTblId uint64, oldParentTblId uint64, newParentTblId uint64) error {
   278  	var childRelation engine.Relation
   279  	var err error
   280  	if childTblId == 0 {
   281  		//fk self refer does not update
   282  		return nil
   283  	} else {
   284  		_, _, childRelation, err = c.e.GetRelationById(c.ctx, c.proc.TxnOperator, childTblId)
   285  		if err != nil {
   286  			return err
   287  		}
   288  	}
   289  	oldCt, err := GetConstraintDef(c.ctx, childRelation)
   290  	if err != nil {
   291  		return err
   292  	}
   293  	for _, ct := range oldCt.Cts {
   294  		if def, ok1 := ct.(*engine.ForeignKeyDef); ok1 {
   295  			for i := 0; i < len(def.Fkeys); i++ {
   296  				fkey := def.Fkeys[i]
   297  				if fkey.ForeignTbl == oldParentTblId {
   298  					for j := 0; j < len(fkey.ForeignCols); j++ {
   299  						if newColDef, ok2 := changColDefMap[fkey.ForeignCols[j]]; ok2 {
   300  							fkey.ForeignCols[j] = newColDef.ColId
   301  						}
   302  					}
   303  					fkey.ForeignTbl = newParentTblId
   304  				}
   305  			}
   306  		}
   307  	}
   308  	return childRelation.UpdateConstraint(c.ctx, oldCt)
   309  }
   310  
   311  func updateNewTableColId(c *Compile, copyRel engine.Relation, changColDefMap map[uint64]*plan.ColDef) error {
   312  	engineDefs, err := copyRel.TableDefs(c.ctx)
   313  	if err != nil {
   314  		return err
   315  	}
   316  	for _, def := range engineDefs {
   317  		if attr, ok := def.(*engine.AttributeDef); ok {
   318  			for _, vColDef := range changColDefMap {
   319  				if vColDef.Name == attr.Attr.Name {
   320  					vColDef.ColId = attr.Attr.ID
   321  					break
   322  				}
   323  			}
   324  		}
   325  	}
   326  	return nil
   327  }
   328  
   329  // restoreNewTableRefChildTbls Restore the original table's foreign key child table ids to the copy table definition
   330  func restoreNewTableRefChildTbls(c *Compile, copyRel engine.Relation, refChildTbls []uint64) error {
   331  	oldCt, err := GetConstraintDef(c.ctx, copyRel)
   332  	if err != nil {
   333  		return err
   334  	}
   335  	oldCt.Cts = append(oldCt.Cts, &engine.RefChildTableDef{
   336  		Tables: refChildTbls,
   337  	})
   338  	return copyRel.UpdateConstraint(c.ctx, oldCt)
   339  }
   340  
   341  // notifyParentTableFkTableIdChange Notify the parent table of changes in the tableid of the foreign key table
   342  func notifyParentTableFkTableIdChange(c *Compile, fkey *plan.ForeignKeyDef, oldTableId uint64) error {
   343  	foreignTblId := fkey.ForeignTbl
   344  	_, _, fatherRelation, err := c.e.GetRelationById(c.ctx, c.proc.TxnOperator, foreignTblId)
   345  	if err != nil {
   346  		return err
   347  	}
   348  	oldCt, err := GetConstraintDef(c.ctx, fatherRelation)
   349  	if err != nil {
   350  		return err
   351  	}
   352  	for _, ct := range oldCt.Cts {
   353  		if def, ok1 := ct.(*engine.RefChildTableDef); ok1 {
   354  			for i := 0; i < len(def.Tables); i++ {
   355  				if def.Tables[i] == oldTableId {
   356  					// delete target element
   357  					def.Tables = append(def.Tables[:i], def.Tables[i+1:]...)
   358  					// Because the length of the slice has become shorter, it is necessary to move i forward
   359  					i--
   360  				}
   361  			}
   362  		}
   363  	}
   364  	return fatherRelation.UpdateConstraint(c.ctx, oldCt)
   365  }