From 32027069940848a7c4ee5e5e577383c68bfcfd4c Mon Sep 17 00:00:00 2001 From: crazycs520 Date: Wed, 18 Dec 2019 11:33:11 +0800 Subject: [PATCH] ddl: check foreign key constraint when drop,modify,change column (#14043) --- ddl/column.go | 11 +++++++++++ ddl/db_test.go | 16 +++++++++++++++- ddl/ddl.go | 4 ++++ ddl/ddl_api.go | 8 ++++++++ go.mod | 2 ++ go.sum | 2 ++ 6 files changed, 42 insertions(+), 1 deletion(-) diff --git a/ddl/column.go b/ddl/column.go index 176e2d2519ec8..e500e6dcd0c36 100644 --- a/ddl/column.go +++ b/ddl/column.go @@ -534,6 +534,17 @@ func isColumnWithIndex(colName string, indices []*model.IndexInfo) bool { return false } +func getColumnForeignKeyInfo(colName string, fkInfos []*model.FKInfo) *model.FKInfo { + for _, fkInfo := range fkInfos { + for _, col := range fkInfo.Cols { + if col.L == colName { + return fkInfo + } + } + } + return nil +} + func allocateColumnID(tblInfo *model.TableInfo) int64 { tblInfo.MaxColumnID++ return tblInfo.MaxColumnID diff --git a/ddl/db_test.go b/ddl/db_test.go index 9dbf1809de9f4..bd95dc725a8a7 100644 --- a/ddl/db_test.go +++ b/ddl/db_test.go @@ -2067,7 +2067,21 @@ func (s *testDBSuite2) TestTableForeignKey(c *C) { s.tk.MustExec("create table t3 (a int, b int);") failSQL = "alter table t1 add foreign key (c) REFERENCES t3(a);" assertErrorCode(c, s.tk, failSQL, tmysql.ErrKeyColumnDoesNotExits) - s.tk.MustExec("drop table if exists t1,t2,t3;") + + // Test drop column with foreign key. + s.tk.MustExec("create table t4 (c int,d int,foreign key (d) references t1 (b));") + failSQL = "alter table t4 drop column d" + s.tk.MustGetErrCode(failSQL, mysql.ErrFkColumnCannotDrop) + // Test change column with foreign key. + failSQL = "alter table t4 change column d e bigint;" + s.tk.MustGetErrCode(failSQL, mysql.ErrFKIncompatibleColumns) + // Test modify column with foreign key. + failSQL = "alter table t4 modify column d bigint;" + s.tk.MustGetErrCode(failSQL, mysql.ErrFKIncompatibleColumns) + s.tk.MustQuery("select count(*) from information_schema.KEY_COLUMN_USAGE;") + s.tk.MustExec("alter table t4 drop foreign key d") + s.tk.MustExec("alter table t4 modify column d bigint;") + s.tk.MustExec("drop table if exists t1,t2,t3,t4;") } func (s *testDBSuite3) TestTruncateTable(c *C) { diff --git a/ddl/ddl.go b/ddl/ddl.go index 5dcaa387a2798..998081d3b71d8 100644 --- a/ddl/ddl.go +++ b/ddl/ddl.go @@ -113,6 +113,8 @@ var ( errTooManyFields = terror.ClassDDL.New(codeTooManyFields, "Too many columns") errInvalidSplitRegionRanges = terror.ClassDDL.New(codeInvalidRanges, "Failed to split region ranges") errReorgPanic = terror.ClassDDL.New(codeReorgWorkerPanic, "reorg worker panic.") + errFkColumnCannotDrop = terror.ClassDDL.New(mysql.ErrFkColumnCannotDrop, mysql.MySQLErrName[mysql.ErrFkColumnCannotDrop]) + errReferencedForeignKey = terror.ClassDDL.New(mysql.ErrFKIncompatibleColumns, mysql.MySQLErrName[mysql.ErrFKIncompatibleColumns]) errOnlyOnRangeListPartition = terror.ClassDDL.New(codeOnlyOnRangeListPartition, mysql.MySQLErrName[mysql.ErrOnlyOnRangeListPartition]) // errWrongKeyColumn is for table column cannot be indexed. @@ -826,6 +828,8 @@ func init() { codeSystemVersioningWrongPartitions: mysql.ErrSystemVersioningWrongPartitions, codeWrongPartitionTypeExpectedSystemTime: mysql.ErrWrongPartitionTypeExpectedSystemTime, codeWrongTypeColumnValue: mysql.ErrWrongTypeColumnValue, + mysql.ErrFkColumnCannotDrop: mysql.ErrFkColumnCannotDrop, + mysql.ErrFKIncompatibleColumns: mysql.ErrFKIncompatibleColumns, } terror.ErrClassToMySQLCodes[terror.ClassDDL] = ddlMySQLErrCodes } diff --git a/ddl/ddl_api.go b/ddl/ddl_api.go index 86a7f1aa364fc..ac75d208ab4c8 100644 --- a/ddl/ddl_api.go +++ b/ddl/ddl_api.go @@ -2571,6 +2571,10 @@ func (d *ddl) getModifiableColumnJob(ctx sessionctx.Context, ident ast.Ident, or return nil, infoschema.ErrColumnExists.GenWithStackByArgs(newColName) } } + // Check the column with foreign key. + if fkInfo := getColumnForeignKeyInfo(originalColName.L, t.Meta().ForeignKeys); fkInfo != nil { + return nil, errReferencedForeignKey.GenWithStackByArgs(originalColName, fkInfo.Name) + } // Constraints in the new column means adding new constraints. Errors should thrown, // which will be done by `processColumnOptions` later. @@ -3379,6 +3383,10 @@ func isDroppableColumn(tblInfo *model.TableInfo, colName model.CIStr) error { if isColumnWithIndex(colName.L, tblInfo.Indices) { return errCantDropColWithIndex.GenWithStack("can't drop column %s with index covered now", colName) } + // Check the column with foreign key. + if fkInfo := getColumnForeignKeyInfo(colName.L, tblInfo.ForeignKeys); fkInfo != nil { + return errFkColumnCannotDrop.GenWithStackByArgs(colName, fkInfo.Name) + } return nil } diff --git a/go.mod b/go.mod index 86d948ce64ddf..6553fac1d50d1 100644 --- a/go.mod +++ b/go.mod @@ -75,3 +75,5 @@ require ( ) go 1.13 + +replace github.com/pingcap/parser => github.com/crazycs520/parser v0.0.0-20191218032222-cb5d742ae9e0 diff --git a/go.sum b/go.sum index 4e64fc2e6d7b2..078633cda7285 100644 --- a/go.sum +++ b/go.sum @@ -24,6 +24,8 @@ github.com/coreos/go-systemd v0.0.0-20181031085051-9002847aa142/go.mod h1:F5haX7 github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f h1:lBNOc5arjvs8E5mO2tbpBpLoyyu8B6e44T7hJy6potg= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/crazycs520/parser v0.0.0-20191218032222-cb5d742ae9e0 h1:3Z4NvkQhzgJsMhY1x7EVwh167Rk49Fwc1/8RoDQ8Yhc= +github.com/crazycs520/parser v0.0.0-20191218032222-cb5d742ae9e0/go.mod h1:xLjI+gnWYexq011WPMEvCNS8rFM9qe1vdojIEzSKPuc= github.com/cznic/mathutil v0.0.0-20181122101859-297441e03548 h1:iwZdTE0PVqJCos1vaoKsclOGD3ADKpshg3SRtYBbwso= github.com/cznic/mathutil v0.0.0-20181122101859-297441e03548/go.mod h1:e6NPNENfs9mPDVNRekM7lKScauxd5kXTr1Mfyig6TDM= github.com/cznic/sortutil v0.0.0-20150617083342-4c7342852e65 h1:hxuZop6tSoOi0sxFzoGGYdRqNrPubyaIf9KoBG9tPiE=