From 829383127c0b44da36508a525776afd03261eb6c Mon Sep 17 00:00:00 2001 From: crazycs Date: Wed, 17 Oct 2018 20:21:10 +0800 Subject: [PATCH 1/2] ddl: add check when create table with foreign key. (#7885) * ddl: add check when create table with foreign key --- ddl/ddl.go | 4 ++-- ddl/ddl_api.go | 3 +++ ddl/ddl_db_test.go | 9 +++++++++ 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/ddl/ddl.go b/ddl/ddl.go index c11ed5f9b4365..61ddd49bf92c5 100644 --- a/ddl/ddl.go +++ b/ddl/ddl.go @@ -89,7 +89,7 @@ var ( errIncorrectPrefixKey = terror.ClassDDL.New(codeIncorrectPrefixKey, "Incorrect prefix key; the used key part isn't a string, the used length is longer than the key part, or the storage engine doesn't support unique prefix keys") errTooLongKey = terror.ClassDDL.New(codeTooLongKey, fmt.Sprintf("Specified key was too long; max key length is %d bytes", maxPrefixLength)) - errKeyColumnDoesNotExits = terror.ClassDDL.New(codeKeyColumnDoesNotExits, "this key column doesn't exist in table") + errKeyColumnDoesNotExits = terror.ClassDDL.New(codeKeyColumnDoesNotExits, mysql.MySQLErrName[mysql.ErrKeyColumnDoesNotExits]) errUnknownTypeLength = terror.ClassDDL.New(codeUnknownTypeLength, "Unknown length for type tp %d") errUnknownFractionLength = terror.ClassDDL.New(codeUnknownFractionLength, "Unknown Length for type tp %d and fraction %d") errInvalidJobVersion = terror.ClassDDL.New(codeInvalidJobVersion, "DDL job with version %d greater than current %d") @@ -541,7 +541,7 @@ const ( codeTooLongIdent = 1059 codeDupKeyName = 1061 codeTooLongKey = 1071 - codeKeyColumnDoesNotExits = 1072 + codeKeyColumnDoesNotExits = mysql.ErrKeyColumnDoesNotExits codeIncorrectPrefixKey = 1089 codeCantRemoveAllFields = 1090 codeCantDropFieldOrKey = 1091 diff --git a/ddl/ddl_api.go b/ddl/ddl_api.go index 85f715161dd6a..5a9dfb1fb1a69 100644 --- a/ddl/ddl_api.go +++ b/ddl/ddl_api.go @@ -633,6 +633,9 @@ func buildTableInfo(ctx sessionctx.Context, d *ddl, tableName model.CIStr, cols fk.RefTable = constr.Refer.Table.Name fk.State = model.StatePublic for _, key := range constr.Keys { + if table.FindCol(cols, key.Column.Name.O) == nil { + return nil, errKeyColumnDoesNotExits.Gen("key column %s doesn't exist in table", key.Column.Name) + } fk.Cols = append(fk.Cols, key.Column.Name) } for _, key := range constr.Refer.IndexColNames { diff --git a/ddl/ddl_db_test.go b/ddl/ddl_db_test.go index 51b9d52da646d..d3cabcad097df 100644 --- a/ddl/ddl_db_test.go +++ b/ddl/ddl_db_test.go @@ -1443,6 +1443,15 @@ func (s *testDBSuite) TestCreateTable(c *C) { c.Assert(err, NotNil) } +func (s *testDBSuite) TestTableForeignKey(c *C) { + s.tk = testkit.NewTestKit(c, s.store) + s.tk.MustExec("use test") + s.tk.MustExec("create table t1 (a int, b int);") + failSQL := "create table t2 (c int, foreign key (a) references t1(a));" + s.testErrorCode(c, failSQL, tmysql.ErrKeyColumnDoesNotExits) + s.tk.MustExec("drop table if exists t1,t2;") +} + func (s *testDBSuite) TestCreateTableWithPartition(c *C) { s.tk.MustExec("use test") s.tk.MustExec(`CREATE TABLE tp (a int) PARTITION BY RANGE(a) ( From e0cd02d8caacdae3b3b043875b213d8145c37338 Mon Sep 17 00:00:00 2001 From: crazycs Date: Wed, 31 Oct 2018 19:11:05 +0800 Subject: [PATCH 2/2] ddl: add check when add foreign key. (#8050) --- ddl/ddl_api.go | 7 +++++-- ddl/ddl_db_test.go | 7 ++++++- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/ddl/ddl_api.go b/ddl/ddl_api.go index 5a9dfb1fb1a69..478f1ccad23eb 100644 --- a/ddl/ddl_api.go +++ b/ddl/ddl_api.go @@ -1713,13 +1713,16 @@ func (d *ddl) CreateIndex(ctx sessionctx.Context, ti ast.Ident, unique bool, ind return errors.Trace(err) } -func buildFKInfo(fkName model.CIStr, keys []*ast.IndexColName, refer *ast.ReferenceDef) (*model.FKInfo, error) { +func buildFKInfo(fkName model.CIStr, keys []*ast.IndexColName, refer *ast.ReferenceDef, cols []*table.Column) (*model.FKInfo, error) { var fkInfo model.FKInfo fkInfo.Name = fkName fkInfo.RefTable = refer.Table.Name fkInfo.Cols = make([]model.CIStr, len(keys)) for i, key := range keys { + if table.FindCol(cols, key.Column.Name.O) == nil { + return nil, errKeyColumnDoesNotExits.Gen("key column %s doesn't exist in table", key.Column.Name) + } fkInfo.Cols[i] = key.Column.Name } @@ -1747,7 +1750,7 @@ func (d *ddl) CreateForeignKey(ctx sessionctx.Context, ti ast.Ident, fkName mode return errors.Trace(infoschema.ErrTableNotExists.GenByArgs(ti.Schema, ti.Name)) } - fkInfo, err := buildFKInfo(fkName, keys, refer) + fkInfo, err := buildFKInfo(fkName, keys, refer, t.Cols()) if err != nil { return errors.Trace(err) } diff --git a/ddl/ddl_db_test.go b/ddl/ddl_db_test.go index d3cabcad097df..a7ce45e60fd7e 100644 --- a/ddl/ddl_db_test.go +++ b/ddl/ddl_db_test.go @@ -1447,9 +1447,14 @@ func (s *testDBSuite) TestTableForeignKey(c *C) { s.tk = testkit.NewTestKit(c, s.store) s.tk.MustExec("use test") s.tk.MustExec("create table t1 (a int, b int);") + // test create table with foreign key. failSQL := "create table t2 (c int, foreign key (a) references t1(a));" s.testErrorCode(c, failSQL, tmysql.ErrKeyColumnDoesNotExits) - s.tk.MustExec("drop table if exists t1,t2;") + // test add foreign key. + s.tk.MustExec("create table t3 (a int, b int);") + failSQL = "alter table t1 add foreign key (c) REFERENCES t3(a);" + s.testErrorCode(c, failSQL, tmysql.ErrKeyColumnDoesNotExits) + s.tk.MustExec("drop table if exists t1,t2,t3;") } func (s *testDBSuite) TestCreateTableWithPartition(c *C) {