From 502eeb48a2111867fd0a939cd0c91e68feb543c8 Mon Sep 17 00:00:00 2001 From: Manan Gupta Date: Mon, 28 Aug 2023 15:30:09 +0530 Subject: [PATCH 01/11] test: add basic function and schema for fk testing Signed-off-by: Manan Gupta --- go/test/endtoend/vtgate/foreignkey/fk_test.go | 42 +++++- .../endtoend/vtgate/foreignkey/main_test.go | 31 ++-- .../vtgate/foreignkey/sharded_schema.sql | 142 ++++++++++++++++++ .../vtgate/foreignkey/sharded_vschema.json | 128 ++++++++++++++++ 4 files changed, 330 insertions(+), 13 deletions(-) diff --git a/go/test/endtoend/vtgate/foreignkey/fk_test.go b/go/test/endtoend/vtgate/foreignkey/fk_test.go index 531aed2642a..079d798904a 100644 --- a/go/test/endtoend/vtgate/foreignkey/fk_test.go +++ b/go/test/endtoend/vtgate/foreignkey/fk_test.go @@ -26,7 +26,8 @@ import ( // TestInsertWithFK tests that insertions work as expected when foreign key management is enabled in Vitess. func TestInsertWithFK(t *testing.T) { - conn, closer := start(t) + mcmp, closer := start(t) + conn := mcmp.VtConn defer closer() // insert some data. @@ -56,7 +57,8 @@ func TestInsertWithFK(t *testing.T) { // TestDeleteWithFK tests that deletions work as expected when foreign key management is enabled in Vitess. func TestDeleteWithFK(t *testing.T) { - conn, closer := start(t) + mcmp, closer := start(t) + conn := mcmp.VtConn defer closer() // insert some data. @@ -85,7 +87,8 @@ func TestDeleteWithFK(t *testing.T) { // TestUpdations tests that update work as expected when foreign key management is enabled in Vitess. func TestUpdateWithFK(t *testing.T) { - conn, closer := start(t) + mcmp, closer := start(t) + conn := mcmp.VtConn defer closer() // insert some data. @@ -118,3 +121,36 @@ func TestUpdateWithFK(t *testing.T) { _ = utils.Exec(t, conn, `update t6 set col1 = 40 where pk = 20`) assert.EqualValues(t, 1, qr.RowsAffected) } + +// TestFkScenarios tests the various foreign key scenarios with different constraints +// and makes sure that Vitess works with them as expected. +func TestFkScenarios(t *testing.T) { + testcases := []struct { + name string + dataQueries []string + dmlQuery string + assertionQueries []string + }{ + {}, + } + + for _, tt := range testcases { + t.Run(tt.name, func(t *testing.T) { + mcmp, closer := start(t) + defer closer() + + // Insert all the data required for running the test. + for _, query := range tt.dataQueries { + mcmp.Exec(query) + } + + // Run the DML query that needs to be tested and verify output with MySQL. + _, _ = mcmp.ExecAllowAndCompareError(tt.dmlQuery) + + // Run the assertion queries and verify we get the expected outputs. + for _, query := range tt.assertionQueries { + mcmp.Exec(query) + } + }) + } +} diff --git a/go/test/endtoend/vtgate/foreignkey/main_test.go b/go/test/endtoend/vtgate/foreignkey/main_test.go index 8cd9cdcadc1..dd4b0456098 100644 --- a/go/test/endtoend/vtgate/foreignkey/main_test.go +++ b/go/test/endtoend/vtgate/foreignkey/main_test.go @@ -17,9 +17,9 @@ limitations under the License. package foreignkey import ( - "context" _ "embed" "flag" + "fmt" "os" "testing" @@ -34,6 +34,7 @@ import ( var ( clusterInstance *cluster.LocalProcessCluster vtParams mysql.ConnParams + mysqlParams mysql.ConnParams shardedKs = "ks" Cell = "test" @@ -80,33 +81,43 @@ func TestMain(m *testing.M) { Port: clusterInstance.VtgateMySQLPort, } + connParams, closer, err := utils.NewMySQL(clusterInstance, shardedKs, shardedSchemaSQL) + if err != nil { + fmt.Println(err) + return 1 + } + defer closer() + mysqlParams = connParams return m.Run() }() os.Exit(exitCode) } -func start(t *testing.T) (*mysql.Conn, func()) { - conn, err := mysql.Connect(context.Background(), &vtParams) +func start(t *testing.T) (utils.MySQLCompare, func()) { + mcmp, err := utils.NewMySQLCompare(t, vtParams, mysqlParams) require.NoError(t, err) deleteAll := func() { - _ = utils.Exec(t, conn, "use `ks/-80`") + _ = utils.Exec(t, mcmp.VtConn, "use `ks/-80`") tables := []string{"t4", "t3", "t2", "t1", "multicol_tbl2", "multicol_tbl1"} + for i := 20; i > 0; i-- { + tables = append(tables, fmt.Sprintf("fk_t%v", i)) + } for _, table := range tables { - _ = utils.Exec(t, conn, "delete from "+table) + _, _ = mcmp.ExecAndIgnore("delete from " + table) } - _ = utils.Exec(t, conn, "use `ks/80-`") + _ = utils.Exec(t, mcmp.VtConn, "use `ks/80-`") for _, table := range tables { - _ = utils.Exec(t, conn, "delete from "+table) + _, _ = mcmp.ExecAndIgnore("delete from " + table) } - _ = utils.Exec(t, conn, "use `ks`") + _ = utils.Exec(t, mcmp.VtConn, "use `ks`") } deleteAll() - return conn, func() { + return mcmp, func() { deleteAll() - conn.Close() + mcmp.Close() cluster.PanicHandler(t) } } diff --git a/go/test/endtoend/vtgate/foreignkey/sharded_schema.sql b/go/test/endtoend/vtgate/foreignkey/sharded_schema.sql index 8475104fecc..d45eae7db6e 100644 --- a/go/test/endtoend/vtgate/foreignkey/sharded_schema.sql +++ b/go/test/endtoend/vtgate/foreignkey/sharded_schema.sql @@ -72,3 +72,145 @@ create table t6 primary key (pk), foreign key (sk, col1) references t5 (sk, col1) on delete restrict on update restrict ) Engine = InnoDB; + +create table fk_t1 +( + id bigint, + col varchar(10), + primary key (id), + index(col) +) Engine = InnoDB; + +create table fk_t2 +( + id bigint, + col varchar(10), + primary key (id), + index(col), + foreign key (col) references fk_t1(col) on delete restrict on update restrict +) Engine = InnoDB; + +create table fk_t3 +( + id bigint, + col varchar(10), + primary key (id), + index(col), + foreign key (col) references fk_t2(col) on delete set null on update set null +) Engine = InnoDB; + +create table fk_t4 +( + id bigint, + col varchar(10), + primary key (id), + index(col), + foreign key (col) references fk_t3(col) on delete set null on update set null +) Engine = InnoDB; + +create table fk_t5 +( + id bigint, + col varchar(10), + primary key (id), + index(col), + foreign key (col) references fk_t4(col) on delete restrict on update restrict +) Engine = InnoDB; + +create table fk_t6 +( + id bigint, + col varchar(10), + primary key (id), + index(col), + foreign key (col) references fk_t3(col) on delete set null on update set null +) Engine = InnoDB; + +create table fk_t7 +( + id bigint, + col varchar(10), + primary key (id), + index(col), + foreign key (col) references fk_t2(col) on delete set null on update set null +) Engine = InnoDB; + +create table fk_t10 +( + id bigint, + col varchar(10), + primary key (id), + index(col) +) Engine = InnoDB; + +create table fk_t11 +( + id bigint, + col varchar(10), + primary key (id), + index(col), + foreign key (col) references fk_t10(col) on delete cascade on update cascade +) Engine = InnoDB; + +create table fk_t12 +( + id bigint, + col varchar(10), + primary key (id), + index(col), + foreign key (col) references fk_t11(col) on delete cascade on update cascade +) Engine = InnoDB; + +create table fk_t13 +( + id bigint, + col varchar(10), + primary key (id), + index(col), + foreign key (col) references fk_t11(col) on delete restrict on update restrict +) Engine = InnoDB; + + +create table fk_t15 +( + id bigint, + col varchar(10), + primary key (id), + index(col) +) Engine = InnoDB; + +create table fk_t16 +( + id bigint, + col varchar(10), + primary key (id), + index(col), + foreign key (col) references fk_t15(col) on delete cascade on update cascade +) Engine = InnoDB; + +create table fk_t17 +( + id bigint, + col varchar(10), + primary key (id), + index(col), + foreign key (col) references fk_t16(col) on delete set null on update set null +) Engine = InnoDB; + +create table fk_t18 +( + id bigint, + col varchar(10), + primary key (id), + index(col), + foreign key (col) references fk_t17(col) on delete cascade on update cascade +) Engine = InnoDB; + +create table fk_t19 +( + id bigint, + col varchar(10), + primary key (id), + index(col), + foreign key (col) references fk_t17(col) on delete set null on update set null +) Engine = InnoDB; \ No newline at end of file diff --git a/go/test/endtoend/vtgate/foreignkey/sharded_vschema.json b/go/test/endtoend/vtgate/foreignkey/sharded_vschema.json index c80662b6852..074f08ce848 100644 --- a/go/test/endtoend/vtgate/foreignkey/sharded_vschema.json +++ b/go/test/endtoend/vtgate/foreignkey/sharded_vschema.json @@ -86,6 +86,134 @@ "name": "multicol_vdx" } ] + }, + "fk_t1": { + "column_vindexes": [ + { + "column": "id", + "name": "xxhash" + } + ] + }, + "fk_t2": { + "column_vindexes": [ + { + "column": "id", + "name": "xxhash" + } + ] + }, + "fk_t3": { + "column_vindexes": [ + { + "column": "id", + "name": "xxhash" + } + ] + }, + "fk_t4": { + "column_vindexes": [ + { + "column": "id", + "name": "xxhash" + } + ] + }, + "fk_t5": { + "column_vindexes": [ + { + "column": "id", + "name": "xxhash" + } + ] + }, + "fk_t6": { + "column_vindexes": [ + { + "column": "id", + "name": "xxhash" + } + ] + }, + "fk_t7": { + "column_vindexes": [ + { + "column": "id", + "name": "xxhash" + } + ] + }, + "fk_t10": { + "column_vindexes": [ + { + "column": "id", + "name": "xxhash" + } + ] + }, + "fk_t11": { + "column_vindexes": [ + { + "column": "id", + "name": "xxhash" + } + ] + }, + "fk_t12": { + "column_vindexes": [ + { + "column": "id", + "name": "xxhash" + } + ] + }, + "fk_t13": { + "column_vindexes": [ + { + "column": "id", + "name": "xxhash" + } + ] + }, + "fk_t15": { + "column_vindexes": [ + { + "column": "id", + "name": "xxhash" + } + ] + }, + "fk_t16": { + "column_vindexes": [ + { + "column": "id", + "name": "xxhash" + } + ] + }, + "fk_t17": { + "column_vindexes": [ + { + "column": "id", + "name": "xxhash" + } + ] + }, + "fk_t18": { + "column_vindexes": [ + { + "column": "id", + "name": "xxhash" + } + ] + }, + "fk_t19": { + "column_vindexes": [ + { + "column": "id", + "name": "xxhash" + } + ] } } } \ No newline at end of file From f99be36305cf10030b06c0fb64f6a3ec0f435a26 Mon Sep 17 00:00:00 2001 From: Manan Gupta Date: Mon, 28 Aug 2023 17:22:47 +0530 Subject: [PATCH 02/11] docs: add comments for the schema being used Signed-off-by: Manan Gupta --- go/test/endtoend/vtgate/foreignkey/fk_test.go | 66 +++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/go/test/endtoend/vtgate/foreignkey/fk_test.go b/go/test/endtoend/vtgate/foreignkey/fk_test.go index 079d798904a..b0a453767ac 100644 --- a/go/test/endtoend/vtgate/foreignkey/fk_test.go +++ b/go/test/endtoend/vtgate/foreignkey/fk_test.go @@ -124,6 +124,72 @@ func TestUpdateWithFK(t *testing.T) { // TestFkScenarios tests the various foreign key scenarios with different constraints // and makes sure that Vitess works with them as expected. +// The test has 3 independent Schema's that are used for testing - +/* + * t1 + * │ + * │ On Delete Restrict + * │ On Update Restrict + * ▼ + * ┌───────────────────t2────────────────┐ + * │ │ + * │On Delete Set Null │ On Delete Set Null + * │On Update Set Null │ On Update Set Null + * ▼ ▼ + * t7 t3───────────────────┐ + * │ │ + * │ │ On Delete Set Null + * On Delete Set Null │ │ On Update Set Null + * On Update Set Null │ │ + * ▼ ▼ + * t4 t6 + * │ + * │ + * On Delete Restrict │ + * On Update Restrict │ + * │ + * ▼ + * t5 + */ +/* + * t10 + * │ + * On Delete Cascade │ + * On Update Cascade │ + * │ + * ▼ + * t11──────────────────┐ + * │ │ + * │ │ On Delete Restrict + * On Delete Cascade │ │ On Update Restrict + * On Update Cascade │ │ + * │ │ + * ▼ ▼ + * t12 t13 + */ +/* + * t15 + * │ + * │ + * On Delete Cascade │ + * On Update Cascade │ + * │ + * ▼ + * t16 + * │ + * On Delete Set Null │ + * On Update Set Null │ + * │ + * ▼ + * t17──────────────────┐ + * │ │ + * │ │ On Delete Set Null + * On Delete Cascade │ │ On Update Set Null + * On Update Cascade │ │ + * │ │ + * ▼ ▼ + * t18 t19 + */ func TestFkScenarios(t *testing.T) { testcases := []struct { name string From 7b3fc552adc0d169fa2af9b34ff1aebf1e1893d6 Mon Sep 17 00:00:00 2001 From: Manan Gupta Date: Mon, 28 Aug 2023 17:34:29 +0530 Subject: [PATCH 03/11] test: add insertion tests and fix schema comment Signed-off-by: Manan Gupta --- go/test/endtoend/vtgate/foreignkey/fk_test.go | 95 ++++++++++++------- 1 file changed, 62 insertions(+), 33 deletions(-) diff --git a/go/test/endtoend/vtgate/foreignkey/fk_test.go b/go/test/endtoend/vtgate/foreignkey/fk_test.go index b0a453767ac..399de5aac93 100644 --- a/go/test/endtoend/vtgate/foreignkey/fk_test.go +++ b/go/test/endtoend/vtgate/foreignkey/fk_test.go @@ -20,6 +20,7 @@ import ( "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "vitess.io/vitess/go/test/endtoend/utils" ) @@ -124,80 +125,108 @@ func TestUpdateWithFK(t *testing.T) { // TestFkScenarios tests the various foreign key scenarios with different constraints // and makes sure that Vitess works with them as expected. -// The test has 3 independent Schema's that are used for testing - +// The test has 3 independent Schemas that are used for testing - /* - * t1 - * │ - * │ On Delete Restrict - * │ On Update Restrict - * ▼ - * ┌───────────────────t2────────────────┐ - * │ │ - * │On Delete Set Null │ On Delete Set Null - * │On Update Set Null │ On Update Set Null - * ▼ ▼ - * t7 t3───────────────────┐ - * │ │ - * │ │ On Delete Set Null - * On Delete Set Null │ │ On Update Set Null - * On Update Set Null │ │ - * ▼ ▼ - * t4 t6 - * │ - * │ - * On Delete Restrict │ - * On Update Restrict │ - * │ - * ▼ - * t5 + * fk_t1 + * │ + * │ On Delete Restrict + * │ On Update Restrict + * ▼ + * ┌────────────────fk_t2────────────────┐ + * │ │ + * │On Delete Set Null │ On Delete Set Null + * │On Update Set Null │ On Update Set Null + * ▼ ▼ + * fk_t7 fk_t3───────────────────┐ + * │ │ + * │ │ On Delete Set Null + * On Delete Set Null │ │ On Update Set Null + * On Update Set Null │ │ + * ▼ ▼ + * fk_t4 fk_t6 + * │ + * │ + * On Delete Restrict │ + * On Update Restrict │ + * │ + * ▼ + * fk_t5 */ /* - * t10 + * fk_t10 * │ * On Delete Cascade │ * On Update Cascade │ * │ * ▼ - * t11──────────────────┐ + * fk_t11──────────────────┐ * │ │ * │ │ On Delete Restrict * On Delete Cascade │ │ On Update Restrict * On Update Cascade │ │ * │ │ * ▼ ▼ - * t12 t13 + * fk_t12 fk_t13 */ /* - * t15 + * fk_t15 * │ * │ * On Delete Cascade │ * On Update Cascade │ * │ * ▼ - * t16 + * fk_t16 * │ * On Delete Set Null │ * On Update Set Null │ * │ * ▼ - * t17──────────────────┐ + * fk_t17──────────────────┐ * │ │ * │ │ On Delete Set Null * On Delete Cascade │ │ On Update Set Null * On Update Cascade │ │ * │ │ * ▼ ▼ - * t18 t19 + * fk_t18 fk_t19 */ func TestFkScenarios(t *testing.T) { + // Wait for schema-tracking to be complete. + err := utils.WaitForColumn(t, clusterInstance.VtgateProcess, "ks", "fk_t1", "col") + require.NoError(t, err) + err = utils.WaitForColumn(t, clusterInstance.VtgateProcess, "ks", "fk_t18", "col") + require.NoError(t, err) + err = utils.WaitForColumn(t, clusterInstance.VtgateProcess, "ks", "fk_t11", "col") + require.NoError(t, err) + testcases := []struct { name string dataQueries []string dmlQuery string assertionQueries []string }{ - {}, + { + name: "Insert failure due to parent key not existing", + dataQueries: []string{ + "insert into fk_t1(id, col) values (1, 5)", + }, + dmlQuery: "insert into t2(id, col) values (1, 7)", + assertionQueries: []string{ + "select * from fk_t1 order by id", + "select * from fk_t2 order by id", + }, + }, { + name: "Insert success", + dataQueries: []string{ + "insert into fk_t1(id, col) values (1, 7)", + }, + dmlQuery: "insert into fk_t2(id, col) values (1, 7)", + assertionQueries: []string{ + "select * from fk_t1 order by id", + "select * from fk_t2 order by id", + }, + }, } for _, tt := range testcases { From 19ec26a23333c8696228831bf24b96ff542dddf2 Mon Sep 17 00:00:00 2001 From: Manan Gupta Date: Mon, 28 Aug 2023 18:11:25 +0530 Subject: [PATCH 04/11] test: add tests for SET NULL constraints Signed-off-by: Manan Gupta --- go/test/endtoend/vtgate/foreignkey/fk_test.go | 149 +++++++++++++++++- 1 file changed, 146 insertions(+), 3 deletions(-) diff --git a/go/test/endtoend/vtgate/foreignkey/fk_test.go b/go/test/endtoend/vtgate/foreignkey/fk_test.go index 399de5aac93..01c62edfaa4 100644 --- a/go/test/endtoend/vtgate/foreignkey/fk_test.go +++ b/go/test/endtoend/vtgate/foreignkey/fk_test.go @@ -123,8 +123,9 @@ func TestUpdateWithFK(t *testing.T) { assert.EqualValues(t, 1, qr.RowsAffected) } -// TestFkScenarios tests the various foreign key scenarios with different constraints -// and makes sure that Vitess works with them as expected. +// TestCrossShardFkScenarios tests the various foreign key scenarios with different constraints +// and makes sure that Vitess works with them as expected. All the tables are sharded in this test +// and all the foreign key constraints are cross-shard ones. // The test has 3 independent Schemas that are used for testing - /* * fk_t1 @@ -191,7 +192,7 @@ func TestUpdateWithFK(t *testing.T) { * ▼ ▼ * fk_t18 fk_t19 */ -func TestFkScenarios(t *testing.T) { +func TestCrossShardFkScenarios(t *testing.T) { // Wait for schema-tracking to be complete. err := utils.WaitForColumn(t, clusterInstance.VtgateProcess, "ks", "fk_t1", "col") require.NoError(t, err) @@ -226,6 +227,148 @@ func TestFkScenarios(t *testing.T) { "select * from fk_t1 order by id", "select * from fk_t2 order by id", }, + }, { + name: "Update failure with restrict foreign keys", + dataQueries: []string{ + "insert into fk_t1(id, col) values (1, 7)", + "insert into fk_t2(id, col) values (1, 7)", + }, + dmlQuery: "update fk_t1 set col = 5 where id = 1", + assertionQueries: []string{ + "select * from fk_t1 order by id", + "select * from fk_t2 order by id", + }, + }, { + name: "Update success with restrict foreign keys", + dataQueries: []string{ + "insert into fk_t1(id, col) values (1, 7), (2, 9)", + "insert into fk_t2(id, col) values (1, 7)", + }, + dmlQuery: "update fk_t1 set col = 5 where id = 2", + assertionQueries: []string{ + "select * from fk_t1 order by id", + "select * from fk_t2 order by id", + }, + }, { + name: "Delete failure with restrict foreign keys", + dataQueries: []string{ + "insert into fk_t1(id, col) values (1, 7)", + "insert into fk_t2(id, col) values (1, 7)", + }, + dmlQuery: "delete from fk_t1 where id = 1", + assertionQueries: []string{ + "select * from fk_t1 order by id", + "select * from fk_t2 order by id", + }, + }, { + name: "Delete success with restrict foreign keys", + dataQueries: []string{ + "insert into fk_t1(id, col) values (1, 7), (2, 9)", + "insert into fk_t2(id, col) values (1, 7)", + }, + dmlQuery: "delete from fk_t1 where id = 2", + assertionQueries: []string{ + "select * from fk_t1 order by id", + "select * from fk_t2 order by id", + }, + }, { + name: "Update success with set null foreign key", + dataQueries: []string{ + "insert into fk_t1(id, col) values (1, 7), (2, 9)", + "insert into fk_t2(id, col) values (1, 7), (2, 9)", + "insert into fk_t3(id, col) values (1, 7), (2, 9)", + "insert into fk_t6(id, col) values (1, 7)", + }, + dmlQuery: "update fk_t3 set col = 9 where id = 1", + assertionQueries: []string{ + "select * from fk_t1 order by id", + "select * from fk_t2 order by id", + "select * from fk_t3 order by id", + "select * from fk_t6 order by id", + }, + }, { + name: "Update failure with set null foreign key with child having a restrict foreign key", + dataQueries: []string{ + "insert into fk_t1(id, col) values (1, 7), (2, 9)", + "insert into fk_t2(id, col) values (1, 7), (2, 9)", + "insert into fk_t3(id, col) values (1, 7), (2, 9)", + "insert into fk_t4(id, col) values (1, 7)", + "insert into fk_t5(id, col) values (1, 7)", + }, + dmlQuery: "update fk_t3 set col = 9 where id = 1", + assertionQueries: []string{ + "select * from fk_t1 order by id", + "select * from fk_t2 order by id", + "select * from fk_t3 order by id", + "select * from fk_t4 order by id", + "select * from fk_t5 order by id", + }, + }, { + name: "Update success with cascaded set nulls", + dataQueries: []string{ + "insert into fk_t1(id, col) values (1, 7), (2, 9)", + "insert into fk_t2(id, col) values (1, 7), (2, 9)", + "insert into fk_t3(id, col) values (1, 7), (2, 9)", + "insert into fk_t4(id, col) values (1, 7), (2, 9)", + "insert into fk_t6(id, col) values (1, 7), (2, 9)", + }, + dmlQuery: "update fk_t2 set col = 9 where id = 1", + assertionQueries: []string{ + "select * from fk_t1 order by id", + "select * from fk_t2 order by id", + "select * from fk_t3 order by id", + "select * from fk_t4 order by id", + "select * from fk_t6 order by id", + }, + }, { + name: "Delete success with set null foreign key", + dataQueries: []string{ + "insert into fk_t1(id, col) values (1, 7), (2, 9)", + "insert into fk_t2(id, col) values (1, 7), (2, 9)", + "insert into fk_t3(id, col) values (1, 7), (2, 9)", + "insert into fk_t6(id, col) values (1, 7)", + }, + dmlQuery: "delete from fk_t3 where id = 1", + assertionQueries: []string{ + "select * from fk_t1 order by id", + "select * from fk_t2 order by id", + "select * from fk_t3 order by id", + "select * from fk_t6 order by id", + }, + }, { + name: "Delete failure with set null foreign key with child having a restrict foreign key", + dataQueries: []string{ + "insert into fk_t1(id, col) values (1, 7), (2, 9)", + "insert into fk_t2(id, col) values (1, 7), (2, 9)", + "insert into fk_t3(id, col) values (1, 7), (2, 9)", + "insert into fk_t4(id, col) values (1, 7)", + "insert into fk_t5(id, col) values (1, 7)", + }, + dmlQuery: "delete from fk_t3 where id = 1", + assertionQueries: []string{ + "select * from fk_t1 order by id", + "select * from fk_t2 order by id", + "select * from fk_t3 order by id", + "select * from fk_t4 order by id", + "select * from fk_t5 order by id", + }, + }, { + name: "Delete success with cascaded set nulls", + dataQueries: []string{ + "insert into fk_t1(id, col) values (1, 7), (2, 9)", + "insert into fk_t2(id, col) values (1, 7), (2, 9)", + "insert into fk_t3(id, col) values (1, 7), (2, 9)", + "insert into fk_t4(id, col) values (1, 7), (2, 9)", + "insert into fk_t6(id, col) values (1, 7), (2, 9)", + }, + dmlQuery: "delete from fk_t2 where id = 1", + assertionQueries: []string{ + "select * from fk_t1 order by id", + "select * from fk_t2 order by id", + "select * from fk_t3 order by id", + "select * from fk_t4 order by id", + "select * from fk_t6 order by id", + }, }, } From f0711d3598b22b007f587ac0ad241ce8e314edb1 Mon Sep 17 00:00:00 2001 From: Manan Gupta Date: Mon, 28 Aug 2023 21:47:59 +0530 Subject: [PATCH 05/11] test: add tests for CASCADE constraints Signed-off-by: Manan Gupta --- go/test/endtoend/vtgate/foreignkey/fk_test.go | 74 +++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/go/test/endtoend/vtgate/foreignkey/fk_test.go b/go/test/endtoend/vtgate/foreignkey/fk_test.go index 01c62edfaa4..1e6d02bb750 100644 --- a/go/test/endtoend/vtgate/foreignkey/fk_test.go +++ b/go/test/endtoend/vtgate/foreignkey/fk_test.go @@ -369,6 +369,80 @@ func TestCrossShardFkScenarios(t *testing.T) { "select * from fk_t4 order by id", "select * from fk_t6 order by id", }, + }, { + name: "Update success with cascade foreign key", + dataQueries: []string{ + "insert into fk_t10(id, col) values (1, 7), (2, 9)", + "insert into fk_t11(id, col) values (1, 7)", + }, + dmlQuery: "update fk_t10 set col = 5 where id = 1", + assertionQueries: []string{ + "select * from fk_t10 order by id", + "select * from fk_t11 order by id", + }, + }, { + name: "Update failure with cascade foreign key with child having a restrict foreign key", + dataQueries: []string{ + "insert into fk_t10(id, col) values (1, 7), (2, 9)", + "insert into fk_t11(id, col) values (1, 7)", + "insert into fk_t13(id, col) values (1, 7)", + }, + dmlQuery: "update fk_t10 set col = 5 where id = 1", + assertionQueries: []string{ + "select * from fk_t10 order by id", + "select * from fk_t11 order by id", + "select * from fk_t13 order by id", + }, + }, { + name: "Update success with cascaded cascade foreign keys", + dataQueries: []string{ + "insert into fk_t10(id, col) values (1, 7), (2, 9)", + "insert into fk_t11(id, col) values (1, 7)", + "insert into fk_t12(id, col) values (1, 7)", + }, + dmlQuery: "update fk_t10 set col = 5 where id = 1", + assertionQueries: []string{ + "select * from fk_t10 order by id", + "select * from fk_t11 order by id", + "select * from fk_t12 order by id", + }, + }, { + name: "Delete success with cascade foreign key", + dataQueries: []string{ + "insert into fk_t10(id, col) values (1, 7), (2, 9)", + "insert into fk_t11(id, col) values (1, 7)", + }, + dmlQuery: "delete from fk_t10 where id = 1", + assertionQueries: []string{ + "select * from fk_t10 order by id", + "select * from fk_t11 order by id", + }, + }, { + name: "Delete failure with cascade foreign key with child having a restrict foreign key", + dataQueries: []string{ + "insert into fk_t10(id, col) values (1, 7), (2, 9)", + "insert into fk_t11(id, col) values (1, 7)", + "insert into fk_t13(id, col) values (1, 7)", + }, + dmlQuery: "delete from fk_t10 where id = 1", + assertionQueries: []string{ + "select * from fk_t10 order by id", + "select * from fk_t11 order by id", + "select * from fk_t13 order by id", + }, + }, { + name: "Delete success with cascaded cascade foreign keys", + dataQueries: []string{ + "insert into fk_t10(id, col) values (1, 7), (2, 9)", + "insert into fk_t11(id, col) values (1, 7)", + "insert into fk_t12(id, col) values (1, 7)", + }, + dmlQuery: "delete from fk_t10 where id = 1", + assertionQueries: []string{ + "select * from fk_t10 order by id", + "select * from fk_t11 order by id", + "select * from fk_t12 order by id", + }, }, } From a43c49f0cbcab1aac4d0e27bc9cf3d17cf98380d Mon Sep 17 00:00:00 2001 From: Manan Gupta Date: Tue, 29 Aug 2023 09:27:32 +0530 Subject: [PATCH 06/11] test: add tests for mixed constraints Signed-off-by: Manan Gupta --- go/test/endtoend/vtgate/foreignkey/fk_test.go | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/go/test/endtoend/vtgate/foreignkey/fk_test.go b/go/test/endtoend/vtgate/foreignkey/fk_test.go index 1e6d02bb750..9a83c7de698 100644 --- a/go/test/endtoend/vtgate/foreignkey/fk_test.go +++ b/go/test/endtoend/vtgate/foreignkey/fk_test.go @@ -443,6 +443,51 @@ func TestCrossShardFkScenarios(t *testing.T) { "select * from fk_t11 order by id", "select * from fk_t12 order by id", }, + }, { + name: "Delete success with set null to an update cascade foreign key", + dataQueries: []string{ + "insert into fk_t15(id, col) values (1, 7), (2, 9)", + "insert into fk_t16(id, col) values (1, 7), (2, 9)", + "insert into fk_t17(id, col) values (1, 7)", + "insert into fk_t18(id, col) values (1, 7)", + }, + dmlQuery: "delete from fk_t16 where id = 1", + assertionQueries: []string{ + "select * from fk_t15 order by id", + "select * from fk_t16 order by id", + "select * from fk_t17 order by id", + "select * from fk_t18 order by id", + }, + }, { + name: "Delete success with cascade to delete with set null to an update set null foreign key", + dataQueries: []string{ + "insert into fk_t15(id, col) values (1, 7), (2, 9)", + "insert into fk_t16(id, col) values (1, 7), (2, 9)", + "insert into fk_t17(id, col) values (1, 7)", + "insert into fk_t19(id, col) values (1, 7)", + }, + dmlQuery: "delete from fk_t15 where id = 1", + assertionQueries: []string{ + "select * from fk_t15 order by id", + "select * from fk_t16 order by id", + "select * from fk_t17 order by id", + "select * from fk_t19 order by id", + }, + }, { + name: "Update success with cascade to an update set null to an update cascade foreign key", + dataQueries: []string{ + "insert into fk_t15(id, col) values (1, 7), (2, 9)", + "insert into fk_t16(id, col) values (1, 7), (2, 9)", + "insert into fk_t17(id, col) values (1, 7)", + "insert into fk_t18(id, col) values (1, 7)", + }, + dmlQuery: "update fk_t15 set col = 3 where id = 1", + assertionQueries: []string{ + "select * from fk_t15 order by id", + "select * from fk_t16 order by id", + "select * from fk_t17 order by id", + "select * from fk_t18 order by id", + }, }, } From 864422dfe557b78379b60822572e3cb90cba1f0c Mon Sep 17 00:00:00 2001 From: Manan Gupta Date: Tue, 29 Aug 2023 10:16:05 +0530 Subject: [PATCH 07/11] test: add test for transactions and check the functionality if a dml fails during it Signed-off-by: Manan Gupta --- go/test/endtoend/vtgate/foreignkey/fk_test.go | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/go/test/endtoend/vtgate/foreignkey/fk_test.go b/go/test/endtoend/vtgate/foreignkey/fk_test.go index 9a83c7de698..cc75d217a83 100644 --- a/go/test/endtoend/vtgate/foreignkey/fk_test.go +++ b/go/test/endtoend/vtgate/foreignkey/fk_test.go @@ -510,4 +510,68 @@ func TestCrossShardFkScenarios(t *testing.T) { } }) } + + t.Run("Transactions with intermediate failure", func(t *testing.T) { + mcmp, closer := start(t) + defer closer() + + // Insert some rows + mcmp.Exec("INSERT INTO fk_t10(id, col) VALUES (1, 7), (2, 9), (3, 5)") + mcmp.Exec("INSERT INTO fk_t11(id, col) VALUES (1, 7), (2, 9), (3, 5)") + mcmp.Exec("INSERT INTO fk_t12(id, col) VALUES (1, 7), (2, 9), (3, 5)") + + // Start a transaction + mcmp.Exec("BEGIN") + + // Insert another row. + mcmp.Exec("INSERT INTO fk_t13(id, col) VALUES (1, 7)") + + // Delete success for cascaded (2, 9) + mcmp.Exec("DELETE FROM fk_t10 WHERE id = 2") + + // Verify the results + mcmp.Exec("SELECT * FROM fk_t10 ORDER BY id") + mcmp.Exec("SELECT * FROM fk_t11 ORDER BY id") + mcmp.Exec("SELECT * FROM fk_t12 ORDER BY id") + mcmp.Exec("SELECT * FROM fk_t13 ORDER BY id") + + // Update that fails + _, err = mcmp.ExecAllowAndCompareError("UPDATE fk_t10 SET col = 15 WHERE id = 1") + require.Error(t, err) + + // Verify the results + // Since we are in a transaction, we still expect the transaction to be ongoing, with no change to the tables + // since the update should fail. + mcmp.Exec("SELECT * FROM fk_t10 ORDER BY id") + mcmp.Exec("SELECT * FROM fk_t11 ORDER BY id") + mcmp.Exec("SELECT * FROM fk_t12 ORDER BY id") + mcmp.Exec("SELECT * FROM fk_t13 ORDER BY id") + + // Update that is a success + mcmp.Exec("UPDATE fk_t10 SET col = 15 where id = 3") + + // Verify the results + mcmp.Exec("SELECT * FROM fk_t10 ORDER BY id") + mcmp.Exec("SELECT * FROM fk_t11 ORDER BY id") + mcmp.Exec("SELECT * FROM fk_t12 ORDER BY id") + mcmp.Exec("SELECT * FROM fk_t13 ORDER BY id") + + // Insert a new row + mcmp.Exec("INSERT INTO fk_t13(id, col) VALUES (3, 15)") + + // Verify the results + mcmp.Exec("SELECT * FROM fk_t10 ORDER BY id") + mcmp.Exec("SELECT * FROM fk_t11 ORDER BY id") + mcmp.Exec("SELECT * FROM fk_t12 ORDER BY id") + mcmp.Exec("SELECT * FROM fk_t13 ORDER BY id") + + // Rollback the transaction. + mcmp.Exec("ROLLBACK") + + // Verify the results + mcmp.Exec("SELECT * FROM fk_t10 ORDER BY id") + mcmp.Exec("SELECT * FROM fk_t11 ORDER BY id") + mcmp.Exec("SELECT * FROM fk_t12 ORDER BY id") + mcmp.Exec("SELECT * FROM fk_t13 ORDER BY id") + }) } From a1dbf4fed483edd2091dc92790c5a03a95c698d7 Mon Sep 17 00:00:00 2001 From: Manan Gupta Date: Tue, 29 Aug 2023 12:59:51 +0530 Subject: [PATCH 08/11] test: run the test for unsharded keyspace too Signed-off-by: Manan Gupta --- go/test/endtoend/vtgate/foreignkey/fk_test.go | 190 ++++++++++-------- .../endtoend/vtgate/foreignkey/main_test.go | 8 +- .../vtgate/foreignkey/unsharded_schema.sql | 142 +++++++++++++ .../vtgate/foreignkey/unsharded_vschema.json | 20 +- 4 files changed, 274 insertions(+), 86 deletions(-) diff --git a/go/test/endtoend/vtgate/foreignkey/fk_test.go b/go/test/endtoend/vtgate/foreignkey/fk_test.go index 82150faa4b7..b9b7b11437f 100644 --- a/go/test/endtoend/vtgate/foreignkey/fk_test.go +++ b/go/test/endtoend/vtgate/foreignkey/fk_test.go @@ -161,9 +161,9 @@ func TestUpdateWithFK(t *testing.T) { utils.AssertMatches(t, conn, `select * from u_t3 order by id`, `[[INT64(1) INT64(12)] [INT64(32) INT64(13)]]`) } -// TestCrossShardFkScenarios tests the various foreign key scenarios with different constraints -// and makes sure that Vitess works with them as expected. All the tables are sharded in this test -// and all the foreign key constraints are cross-shard ones. +// TestFkScenarios tests the various foreign key scenarios with different constraints +// and makes sure that Vitess works with them as expected. All the tables are present in both sharded and unsharded keyspace +// and all the foreign key constraints are cross-shard ones for the sharded keyspace. // The test has 3 independent Schemas that are used for testing - /* * fk_t1 @@ -230,13 +230,19 @@ func TestUpdateWithFK(t *testing.T) { * ▼ ▼ * fk_t18 fk_t19 */ -func TestCrossShardFkScenarios(t *testing.T) { +func TestFkScenarios(t *testing.T) { // Wait for schema-tracking to be complete. - err := utils.WaitForColumn(t, clusterInstance.VtgateProcess, "ks", "fk_t1", "col") + err := utils.WaitForColumn(t, clusterInstance.VtgateProcess, shardedKs, "fk_t1", "col") require.NoError(t, err) - err = utils.WaitForColumn(t, clusterInstance.VtgateProcess, "ks", "fk_t18", "col") + err = utils.WaitForColumn(t, clusterInstance.VtgateProcess, shardedKs, "fk_t18", "col") require.NoError(t, err) - err = utils.WaitForColumn(t, clusterInstance.VtgateProcess, "ks", "fk_t11", "col") + err = utils.WaitForColumn(t, clusterInstance.VtgateProcess, shardedKs, "fk_t11", "col") + require.NoError(t, err) + err = utils.WaitForColumn(t, clusterInstance.VtgateProcess, unshardedKs, "fk_t1", "col") + require.NoError(t, err) + err = utils.WaitForColumn(t, clusterInstance.VtgateProcess, unshardedKs, "fk_t18", "col") + require.NoError(t, err) + err = utils.WaitForColumn(t, clusterInstance.VtgateProcess, unshardedKs, "fk_t11", "col") require.NoError(t, err) testcases := []struct { @@ -530,86 +536,106 @@ func TestCrossShardFkScenarios(t *testing.T) { } for _, tt := range testcases { - t.Run(tt.name, func(t *testing.T) { + for _, testSharded := range []bool{false, true} { + t.Run(getTestName(tt.name, testSharded), func(t *testing.T) { + mcmp, closer := start(t) + defer closer() + // To test unsharded case in vtgate, we just change the keyspace we use for the test. + if !testSharded { + _ = utils.Exec(t, mcmp.VtConn, "use `uks`") + } + + // Insert all the data required for running the test. + for _, query := range tt.dataQueries { + mcmp.Exec(query) + } + + // Run the DML query that needs to be tested and verify output with MySQL. + _, _ = mcmp.ExecAllowAndCompareError(tt.dmlQuery) + + // Run the assertion queries and verify we get the expected outputs. + for _, query := range tt.assertionQueries { + mcmp.Exec(query) + } + }) + } + } + + for _, testSharded := range []bool{false, true} { + t.Run(getTestName("Transactions with intermediate failure", testSharded), func(t *testing.T) { mcmp, closer := start(t) defer closer() - - // Insert all the data required for running the test. - for _, query := range tt.dataQueries { - mcmp.Exec(query) + // To test unsharded case in vtgate, we just change the keyspace we use for the test. + if !testSharded { + _ = utils.Exec(t, mcmp.VtConn, "use `uks`") } - // Run the DML query that needs to be tested and verify output with MySQL. - _, _ = mcmp.ExecAllowAndCompareError(tt.dmlQuery) - - // Run the assertion queries and verify we get the expected outputs. - for _, query := range tt.assertionQueries { - mcmp.Exec(query) - } + // Insert some rows + mcmp.Exec("INSERT INTO fk_t10(id, col) VALUES (1, 7), (2, 9), (3, 5)") + mcmp.Exec("INSERT INTO fk_t11(id, col) VALUES (1, 7), (2, 9), (3, 5)") + mcmp.Exec("INSERT INTO fk_t12(id, col) VALUES (1, 7), (2, 9), (3, 5)") + + // Start a transaction + mcmp.Exec("BEGIN") + + // Insert another row. + mcmp.Exec("INSERT INTO fk_t13(id, col) VALUES (1, 7)") + + // Delete success for cascaded (2, 9) + mcmp.Exec("DELETE FROM fk_t10 WHERE id = 2") + + // Verify the results + mcmp.Exec("SELECT * FROM fk_t10 ORDER BY id") + mcmp.Exec("SELECT * FROM fk_t11 ORDER BY id") + mcmp.Exec("SELECT * FROM fk_t12 ORDER BY id") + mcmp.Exec("SELECT * FROM fk_t13 ORDER BY id") + + // Update that fails + _, err = mcmp.ExecAllowAndCompareError("UPDATE fk_t10 SET col = 15 WHERE id = 1") + require.Error(t, err) + + // Verify the results + // Since we are in a transaction, we still expect the transaction to be ongoing, with no change to the tables + // since the update should fail. + mcmp.Exec("SELECT * FROM fk_t10 ORDER BY id") + mcmp.Exec("SELECT * FROM fk_t11 ORDER BY id") + mcmp.Exec("SELECT * FROM fk_t12 ORDER BY id") + mcmp.Exec("SELECT * FROM fk_t13 ORDER BY id") + + // Update that is a success + mcmp.Exec("UPDATE fk_t10 SET col = 15 where id = 3") + + // Verify the results + mcmp.Exec("SELECT * FROM fk_t10 ORDER BY id") + mcmp.Exec("SELECT * FROM fk_t11 ORDER BY id") + mcmp.Exec("SELECT * FROM fk_t12 ORDER BY id") + mcmp.Exec("SELECT * FROM fk_t13 ORDER BY id") + + // Insert a new row + mcmp.Exec("INSERT INTO fk_t13(id, col) VALUES (3, 15)") + + // Verify the results + mcmp.Exec("SELECT * FROM fk_t10 ORDER BY id") + mcmp.Exec("SELECT * FROM fk_t11 ORDER BY id") + mcmp.Exec("SELECT * FROM fk_t12 ORDER BY id") + mcmp.Exec("SELECT * FROM fk_t13 ORDER BY id") + + // Rollback the transaction. + mcmp.Exec("ROLLBACK") + + // Verify the results + mcmp.Exec("SELECT * FROM fk_t10 ORDER BY id") + mcmp.Exec("SELECT * FROM fk_t11 ORDER BY id") + mcmp.Exec("SELECT * FROM fk_t12 ORDER BY id") + mcmp.Exec("SELECT * FROM fk_t13 ORDER BY id") }) } +} - t.Run("Transactions with intermediate failure", func(t *testing.T) { - mcmp, closer := start(t) - defer closer() - - // Insert some rows - mcmp.Exec("INSERT INTO fk_t10(id, col) VALUES (1, 7), (2, 9), (3, 5)") - mcmp.Exec("INSERT INTO fk_t11(id, col) VALUES (1, 7), (2, 9), (3, 5)") - mcmp.Exec("INSERT INTO fk_t12(id, col) VALUES (1, 7), (2, 9), (3, 5)") - - // Start a transaction - mcmp.Exec("BEGIN") - - // Insert another row. - mcmp.Exec("INSERT INTO fk_t13(id, col) VALUES (1, 7)") - - // Delete success for cascaded (2, 9) - mcmp.Exec("DELETE FROM fk_t10 WHERE id = 2") - - // Verify the results - mcmp.Exec("SELECT * FROM fk_t10 ORDER BY id") - mcmp.Exec("SELECT * FROM fk_t11 ORDER BY id") - mcmp.Exec("SELECT * FROM fk_t12 ORDER BY id") - mcmp.Exec("SELECT * FROM fk_t13 ORDER BY id") - - // Update that fails - _, err = mcmp.ExecAllowAndCompareError("UPDATE fk_t10 SET col = 15 WHERE id = 1") - require.Error(t, err) - - // Verify the results - // Since we are in a transaction, we still expect the transaction to be ongoing, with no change to the tables - // since the update should fail. - mcmp.Exec("SELECT * FROM fk_t10 ORDER BY id") - mcmp.Exec("SELECT * FROM fk_t11 ORDER BY id") - mcmp.Exec("SELECT * FROM fk_t12 ORDER BY id") - mcmp.Exec("SELECT * FROM fk_t13 ORDER BY id") - - // Update that is a success - mcmp.Exec("UPDATE fk_t10 SET col = 15 where id = 3") - - // Verify the results - mcmp.Exec("SELECT * FROM fk_t10 ORDER BY id") - mcmp.Exec("SELECT * FROM fk_t11 ORDER BY id") - mcmp.Exec("SELECT * FROM fk_t12 ORDER BY id") - mcmp.Exec("SELECT * FROM fk_t13 ORDER BY id") - - // Insert a new row - mcmp.Exec("INSERT INTO fk_t13(id, col) VALUES (3, 15)") - - // Verify the results - mcmp.Exec("SELECT * FROM fk_t10 ORDER BY id") - mcmp.Exec("SELECT * FROM fk_t11 ORDER BY id") - mcmp.Exec("SELECT * FROM fk_t12 ORDER BY id") - mcmp.Exec("SELECT * FROM fk_t13 ORDER BY id") - - // Rollback the transaction. - mcmp.Exec("ROLLBACK") - - // Verify the results - mcmp.Exec("SELECT * FROM fk_t10 ORDER BY id") - mcmp.Exec("SELECT * FROM fk_t11 ORDER BY id") - mcmp.Exec("SELECT * FROM fk_t12 ORDER BY id") - mcmp.Exec("SELECT * FROM fk_t13 ORDER BY id") - }) +// getTestName prepends whether the test is for a sharded keyspace or not to the test name. +func getTestName(testName string, testSharded bool) string { + if testSharded { + return "Sharded - " + testName + } + return "Unsharded - " + testName } diff --git a/go/test/endtoend/vtgate/foreignkey/main_test.go b/go/test/endtoend/vtgate/foreignkey/main_test.go index 376ae7bd60d..cf0c76b5404 100644 --- a/go/test/endtoend/vtgate/foreignkey/main_test.go +++ b/go/test/endtoend/vtgate/foreignkey/main_test.go @@ -132,8 +132,12 @@ func start(t *testing.T) (utils.MySQLCompare, func()) { _, _ = mcmp.ExecAndIgnore("delete from " + table) } _ = utils.Exec(t, mcmp.VtConn, "use `uks`") - for _, table := range []string{"u_t1", "u_t2"} { - _ = utils.Exec(t, mcmp.VtConn, "delete from "+table) + tables = []string{"u_t1", "u_t2", "u_t3"} + for i := 20; i > 0; i-- { + tables = append(tables, fmt.Sprintf("fk_t%v", i)) + } + for _, table := range tables { + _, _ = mcmp.ExecAndIgnore("delete from " + table) } _ = utils.Exec(t, mcmp.VtConn, "use `ks`") } diff --git a/go/test/endtoend/vtgate/foreignkey/unsharded_schema.sql b/go/test/endtoend/vtgate/foreignkey/unsharded_schema.sql index 9c21510046b..bae50b1918e 100644 --- a/go/test/endtoend/vtgate/foreignkey/unsharded_schema.sql +++ b/go/test/endtoend/vtgate/foreignkey/unsharded_schema.sql @@ -20,4 +20,146 @@ create table u_t3 col3 bigint, primary key (id), foreign key (col3) references u_t1 (col1) on delete cascade on update cascade +) Engine = InnoDB; + +create table fk_t1 +( + id bigint, + col varchar(10), + primary key (id), + index(col) +) Engine = InnoDB; + +create table fk_t2 +( + id bigint, + col varchar(10), + primary key (id), + index(col), + foreign key (col) references fk_t1(col) on delete restrict on update restrict +) Engine = InnoDB; + +create table fk_t3 +( + id bigint, + col varchar(10), + primary key (id), + index(col), + foreign key (col) references fk_t2(col) on delete set null on update set null +) Engine = InnoDB; + +create table fk_t4 +( + id bigint, + col varchar(10), + primary key (id), + index(col), + foreign key (col) references fk_t3(col) on delete set null on update set null +) Engine = InnoDB; + +create table fk_t5 +( + id bigint, + col varchar(10), + primary key (id), + index(col), + foreign key (col) references fk_t4(col) on delete restrict on update restrict +) Engine = InnoDB; + +create table fk_t6 +( + id bigint, + col varchar(10), + primary key (id), + index(col), + foreign key (col) references fk_t3(col) on delete set null on update set null +) Engine = InnoDB; + +create table fk_t7 +( + id bigint, + col varchar(10), + primary key (id), + index(col), + foreign key (col) references fk_t2(col) on delete set null on update set null +) Engine = InnoDB; + +create table fk_t10 +( + id bigint, + col varchar(10), + primary key (id), + index(col) +) Engine = InnoDB; + +create table fk_t11 +( + id bigint, + col varchar(10), + primary key (id), + index(col), + foreign key (col) references fk_t10(col) on delete cascade on update cascade +) Engine = InnoDB; + +create table fk_t12 +( + id bigint, + col varchar(10), + primary key (id), + index(col), + foreign key (col) references fk_t11(col) on delete cascade on update cascade +) Engine = InnoDB; + +create table fk_t13 +( + id bigint, + col varchar(10), + primary key (id), + index(col), + foreign key (col) references fk_t11(col) on delete restrict on update restrict +) Engine = InnoDB; + + +create table fk_t15 +( + id bigint, + col varchar(10), + primary key (id), + index(col) +) Engine = InnoDB; + +create table fk_t16 +( + id bigint, + col varchar(10), + primary key (id), + index(col), + foreign key (col) references fk_t15(col) on delete cascade on update cascade +) Engine = InnoDB; + +create table fk_t17 +( + id bigint, + col varchar(10), + primary key (id), + index(col), + foreign key (col) references fk_t16(col) on delete set null on update set null +) Engine = InnoDB; + +create table fk_t18 +( + id bigint, + col varchar(10), + primary key (id), + index(col), + foreign key (col) references fk_t17(col) on delete cascade on update cascade +) Engine = InnoDB; + +create table fk_t19 +( + id bigint, + col varchar(10), + primary key (id), + index(col), + foreign key (col) references fk_t17(col) on delete set null on update set null ) Engine = InnoDB; \ No newline at end of file diff --git a/go/test/endtoend/vtgate/foreignkey/unsharded_vschema.json b/go/test/endtoend/vtgate/foreignkey/unsharded_vschema.json index f46ecbe83c3..c0d2368849f 100644 --- a/go/test/endtoend/vtgate/foreignkey/unsharded_vschema.json +++ b/go/test/endtoend/vtgate/foreignkey/unsharded_vschema.json @@ -2,7 +2,23 @@ "sharded": false, "foreignKeyMode": "FK_MANAGED", "tables": { - "u_a": {}, - "u_b": {} + "u_t1": {}, + "u_t2": {}, + "fk_t1": {}, + "fk_t2": {}, + "fk_t3": {}, + "fk_t4": {}, + "fk_t5": {}, + "fk_t6": {}, + "fk_t7": {}, + "fk_t10": {}, + "fk_t11": {}, + "fk_t12": {}, + "fk_t13": {}, + "fk_t15": {}, + "fk_t16": {}, + "fk_t17": {}, + "fk_t18": {}, + "fk_t19": {} } } \ No newline at end of file From 11383cef30bd814262523ac8231c252a43e6f921 Mon Sep 17 00:00:00 2001 From: Manan Gupta Date: Thu, 31 Aug 2023 09:01:52 +0530 Subject: [PATCH 09/11] comments: add the ascii charts in the schema.sql as well Signed-off-by: Manan Gupta --- .../vtgate/foreignkey/sharded_schema.sql | 67 ++++++++++++++++++ .../vtgate/foreignkey/unsharded_schema.sql | 68 +++++++++++++++++++ 2 files changed, 135 insertions(+) diff --git a/go/test/endtoend/vtgate/foreignkey/sharded_schema.sql b/go/test/endtoend/vtgate/foreignkey/sharded_schema.sql index d45eae7db6e..c6df11261f7 100644 --- a/go/test/endtoend/vtgate/foreignkey/sharded_schema.sql +++ b/go/test/endtoend/vtgate/foreignkey/sharded_schema.sql @@ -73,6 +73,33 @@ create table t6 foreign key (sk, col1) references t5 (sk, col1) on delete restrict on update restrict ) Engine = InnoDB; +/* + * fk_t1 + * │ + * │ On Delete Restrict + * │ On Update Restrict + * ▼ + * ┌────────────────fk_t2────────────────┐ + * │ │ + * │On Delete Set Null │ On Delete Set Null + * │On Update Set Null │ On Update Set Null + * ▼ ▼ + * fk_t7 fk_t3───────────────────┐ + * │ │ + * │ │ On Delete Set Null + * On Delete Set Null │ │ On Update Set Null + * On Update Set Null │ │ + * ▼ ▼ + * fk_t4 fk_t6 + * │ + * │ + * On Delete Restrict │ + * On Update Restrict │ + * │ + * ▼ + * fk_t5 + */ + create table fk_t1 ( id bigint, @@ -135,6 +162,23 @@ create table fk_t7 foreign key (col) references fk_t2(col) on delete set null on update set null ) Engine = InnoDB; +/* + * fk_t10 + * │ + * On Delete Cascade │ + * On Update Cascade │ + * │ + * ▼ + * fk_t11──────────────────┐ + * │ │ + * │ │ On Delete Restrict + * On Delete Cascade │ │ On Update Restrict + * On Update Cascade │ │ + * │ │ + * ▼ ▼ + * fk_t12 fk_t13 + */ + create table fk_t10 ( id bigint, @@ -170,6 +214,29 @@ create table fk_t13 foreign key (col) references fk_t11(col) on delete restrict on update restrict ) Engine = InnoDB; +/* + * fk_t15 + * │ + * │ + * On Delete Cascade │ + * On Update Cascade │ + * │ + * ▼ + * fk_t16 + * │ + * On Delete Set Null │ + * On Update Set Null │ + * │ + * ▼ + * fk_t17──────────────────┐ + * │ │ + * │ │ On Delete Set Null + * On Delete Cascade │ │ On Update Set Null + * On Update Cascade │ │ + * │ │ + * ▼ ▼ + * fk_t18 fk_t19 + */ create table fk_t15 ( diff --git a/go/test/endtoend/vtgate/foreignkey/unsharded_schema.sql b/go/test/endtoend/vtgate/foreignkey/unsharded_schema.sql index bae50b1918e..b3067483b2a 100644 --- a/go/test/endtoend/vtgate/foreignkey/unsharded_schema.sql +++ b/go/test/endtoend/vtgate/foreignkey/unsharded_schema.sql @@ -22,6 +22,34 @@ create table u_t3 foreign key (col3) references u_t1 (col1) on delete cascade on update cascade ) Engine = InnoDB; + +/* + * fk_t1 + * │ + * │ On Delete Restrict + * │ On Update Restrict + * ▼ + * ┌────────────────fk_t2────────────────┐ + * │ │ + * │On Delete Set Null │ On Delete Set Null + * │On Update Set Null │ On Update Set Null + * ▼ ▼ + * fk_t7 fk_t3───────────────────┐ + * │ │ + * │ │ On Delete Set Null + * On Delete Set Null │ │ On Update Set Null + * On Update Set Null │ │ + * ▼ ▼ + * fk_t4 fk_t6 + * │ + * │ + * On Delete Restrict │ + * On Update Restrict │ + * │ + * ▼ + * fk_t5 + */ + create table fk_t1 ( id bigint, @@ -84,6 +112,23 @@ create table fk_t7 foreign key (col) references fk_t2(col) on delete set null on update set null ) Engine = InnoDB; +/* + * fk_t10 + * │ + * On Delete Cascade │ + * On Update Cascade │ + * │ + * ▼ + * fk_t11──────────────────┐ + * │ │ + * │ │ On Delete Restrict + * On Delete Cascade │ │ On Update Restrict + * On Update Cascade │ │ + * │ │ + * ▼ ▼ + * fk_t12 fk_t13 + */ + create table fk_t10 ( id bigint, @@ -119,6 +164,29 @@ create table fk_t13 foreign key (col) references fk_t11(col) on delete restrict on update restrict ) Engine = InnoDB; +/* + * fk_t15 + * │ + * │ + * On Delete Cascade │ + * On Update Cascade │ + * │ + * ▼ + * fk_t16 + * │ + * On Delete Set Null │ + * On Update Set Null │ + * │ + * ▼ + * fk_t17──────────────────┐ + * │ │ + * │ │ On Delete Set Null + * On Delete Cascade │ │ On Update Set Null + * On Update Cascade │ │ + * │ │ + * ▼ ▼ + * fk_t18 fk_t19 + */ create table fk_t15 ( From 39f44d349b9f8a3153a103af299c3b752109250a Mon Sep 17 00:00:00 2001 From: Manan Gupta Date: Thu, 31 Aug 2023 09:35:00 +0530 Subject: [PATCH 10/11] test: add test case for self-referenced fks Signed-off-by: Manan Gupta --- go/test/endtoend/vtgate/foreignkey/fk_test.go | 35 ++++++++++++++++--- .../vtgate/foreignkey/sharded_schema.sql | 14 ++++++++ .../vtgate/foreignkey/unsharded_schema.sql | 14 ++++++++ 3 files changed, 58 insertions(+), 5 deletions(-) diff --git a/go/test/endtoend/vtgate/foreignkey/fk_test.go b/go/test/endtoend/vtgate/foreignkey/fk_test.go index b9b7b11437f..b1ecfb6d3d2 100644 --- a/go/test/endtoend/vtgate/foreignkey/fk_test.go +++ b/go/test/endtoend/vtgate/foreignkey/fk_test.go @@ -164,7 +164,7 @@ func TestUpdateWithFK(t *testing.T) { // TestFkScenarios tests the various foreign key scenarios with different constraints // and makes sure that Vitess works with them as expected. All the tables are present in both sharded and unsharded keyspace // and all the foreign key constraints are cross-shard ones for the sharded keyspace. -// The test has 3 independent Schemas that are used for testing - +// The test has 4 independent Schemas that are used for testing - /* * fk_t1 * │ @@ -230,6 +230,9 @@ func TestUpdateWithFK(t *testing.T) { * ▼ ▼ * fk_t18 fk_t19 */ +/* + Self referenced foreign key from col2 to col in fk_t20 +*/ func TestFkScenarios(t *testing.T) { // Wait for schema-tracking to be complete. err := utils.WaitForColumn(t, clusterInstance.VtgateProcess, shardedKs, "fk_t1", "col") @@ -532,6 +535,24 @@ func TestFkScenarios(t *testing.T) { "select * from fk_t17 order by id", "select * from fk_t18 order by id", }, + }, { + name: "Insert success for self-referenced foreign key", + dataQueries: []string{ + "insert into fk_t20(id, col, col2) values (1, 7, NULL)", + }, + dmlQuery: "insert into fk_t20(id, col, col2) values (2, 9, 7), (3, 10, 9)", + assertionQueries: []string{ + "select * from fk_t20 order by id", + }, + }, { + name: "Insert failure for self-referenced foreign key", + dataQueries: []string{ + "insert into fk_t20(id, col, col2) values (5, 7, NULL)", + }, + dmlQuery: "insert into fk_t20(id, col, col2) values (6, 9, 6)", + assertionQueries: []string{ + "select * from fk_t20 order by id", + }, }, } @@ -540,8 +561,10 @@ func TestFkScenarios(t *testing.T) { t.Run(getTestName(tt.name, testSharded), func(t *testing.T) { mcmp, closer := start(t) defer closer() - // To test unsharded case in vtgate, we just change the keyspace we use for the test. - if !testSharded { + // Set the correct keyspace to use from VtGates. + if testSharded { + _ = utils.Exec(t, mcmp.VtConn, "use `ks`") + } else { _ = utils.Exec(t, mcmp.VtConn, "use `uks`") } @@ -565,8 +588,10 @@ func TestFkScenarios(t *testing.T) { t.Run(getTestName("Transactions with intermediate failure", testSharded), func(t *testing.T) { mcmp, closer := start(t) defer closer() - // To test unsharded case in vtgate, we just change the keyspace we use for the test. - if !testSharded { + // Set the correct keyspace to use from VtGates. + if testSharded { + _ = utils.Exec(t, mcmp.VtConn, "use `ks`") + } else { _ = utils.Exec(t, mcmp.VtConn, "use `uks`") } diff --git a/go/test/endtoend/vtgate/foreignkey/sharded_schema.sql b/go/test/endtoend/vtgate/foreignkey/sharded_schema.sql index c6df11261f7..b530c982904 100644 --- a/go/test/endtoend/vtgate/foreignkey/sharded_schema.sql +++ b/go/test/endtoend/vtgate/foreignkey/sharded_schema.sql @@ -280,4 +280,18 @@ create table fk_t19 primary key (id), index(col), foreign key (col) references fk_t17(col) on delete set null on update set null +) Engine = InnoDB; + +/* + Self referenced foreign key from col2 to col in fk_t20 +*/ + +create table fk_t20 +( + id bigint, + col varchar(10), + col2 varchar(10), + primary key (id), + index(col), + foreign key (col2) references fk_t20(col) on delete restrict on update restrict ) Engine = InnoDB; \ No newline at end of file diff --git a/go/test/endtoend/vtgate/foreignkey/unsharded_schema.sql b/go/test/endtoend/vtgate/foreignkey/unsharded_schema.sql index b3067483b2a..dc6cba7bb08 100644 --- a/go/test/endtoend/vtgate/foreignkey/unsharded_schema.sql +++ b/go/test/endtoend/vtgate/foreignkey/unsharded_schema.sql @@ -230,4 +230,18 @@ create table fk_t19 primary key (id), index(col), foreign key (col) references fk_t17(col) on delete set null on update set null +) Engine = InnoDB; + +/* + Self referenced foreign key from col2 to col in fk_t20 +*/ + +create table fk_t20 +( + id bigint, + col varchar(10), + col2 varchar(10), + primary key (id), + index(col), + foreign key (col2) references fk_t20(col) on delete restrict on update restrict ) Engine = InnoDB; \ No newline at end of file From a6cbc4dce136f5aaf553123658ab48f629f74da9 Mon Sep 17 00:00:00 2001 From: Manan Gupta Date: Wed, 13 Sep 2023 10:48:19 +0530 Subject: [PATCH 11/11] feat: skip sharded tests for now Signed-off-by: Manan Gupta --- go/test/endtoend/vtgate/foreignkey/fk_test.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/go/test/endtoend/vtgate/foreignkey/fk_test.go b/go/test/endtoend/vtgate/foreignkey/fk_test.go index a5b0f0351a4..3f59a247273 100644 --- a/go/test/endtoend/vtgate/foreignkey/fk_test.go +++ b/go/test/endtoend/vtgate/foreignkey/fk_test.go @@ -564,6 +564,7 @@ func TestFkScenarios(t *testing.T) { defer closer() // Set the correct keyspace to use from VtGates. if testSharded { + t.Skip("Skip test since we don't have sharded foreign key support yet") _ = utils.Exec(t, mcmp.VtConn, "use `ks`") } else { _ = utils.Exec(t, mcmp.VtConn, "use `uks`") @@ -591,6 +592,7 @@ func TestFkScenarios(t *testing.T) { defer closer() // Set the correct keyspace to use from VtGates. if testSharded { + t.Skip("Skip test since we don't have sharded foreign key support yet") _ = utils.Exec(t, mcmp.VtConn, "use `ks`") } else { _ = utils.Exec(t, mcmp.VtConn, "use `uks`")