From 302873775c2470bbcdd6d78f2401e896860606f2 Mon Sep 17 00:00:00 2001
From: Nikita Pettik <korablev@tarantool.org>
Date: Mon, 4 Jun 2018 21:29:59 +0300
Subject: [PATCH] sql: prohibit creation of FK on unexisting tables

Originally, SQLite allows to create table with foreign keys constraint
which refers to yet not created parent table. For instance:

CREATE TABLE child(id INT PRIMARY KEY REFERENCES parent);
CREATE TABLE parent(id INT PRIMARY KEY);

This patch bans such ability since it contradicts SQL ANSI.
Moreover, SQLite allows to drop parent table if deletion of all rows
wouldn't result in FK constraint violations. This feature has been
removed since in such situation child table would become inconsistent.

Finally, within current patch ability to create FK constraints on VIEWs
is banned as well.

Part of #3271
---
 src/box/sql/build.c                           |  43 ++++--
 src/box/sql/fkey.c                            |  96 ++------------
 src/box/sql/sqliteInt.h                       |   3 -
 src/box/sql/vdbe.c                            |  30 -----
 test/sql-tap/alter.test.lua                   |   4 +-
 test/sql-tap/fkey1.test.lua                   |  18 +--
 test/sql-tap/fkey2.test.lua                   |  90 ++++---------
 test/sql-tap/fkey3.test.lua                   |   4 +-
 .../gh-2953-drop-table-with-FK.test.lua       | 122 ------------------
 test/sql-tap/table.test.lua                   |   1 +
 test/sql-tap/tkt-b1d3a2e531.test.lua          |   2 +-
 11 files changed, 80 insertions(+), 333 deletions(-)
 delete mode 100755 test/sql-tap/gh-2953-drop-table-with-FK.test.lua

diff --git a/src/box/sql/build.c b/src/box/sql/build.c
index d812eb9a5c..4c99a85e00 100644
--- a/src/box/sql/build.c
+++ b/src/box/sql/build.c
@@ -2072,10 +2072,16 @@ sql_drop_table(struct Parse *parse_context, struct SrcList *table_name_list,
 	 *    removing indexes from _index space and eventually
 	 *    tuple with corresponding space_id from _space.
 	 */
-
-	sql_clear_stat_spaces(parse_context, space_name, NULL);
 	struct Table *tab = sqlite3HashFind(&db->pSchema->tblHash, space_name);
-	sqlite3FkDropTable(parse_context, table_name_list, tab);
+	struct FKey *fk = sqlite3FkReferences(tab);
+	if (fk != NULL && fk->pFrom->def->id != tab->def->id) {
+		diag_set(ClientError, ER_DROP_SPACE, space_name,
+			 "other objects depend on it");
+		parse_context->rc = SQL_TARANTOOL_ERROR;
+		parse_context->nErr++;
+		goto exit_drop_table;
+	}
+	sql_clear_stat_spaces(parse_context, space_name, NULL);
 	sql_code_drop_table(parse_context, space, is_view);
 
  exit_drop_table:
@@ -2117,6 +2123,26 @@ sqlite3CreateForeignKey(Parse * pParse,	/* Parsing context */
 	char *z;
 
 	assert(pTo != 0);
+	char *normalized_name = strndup(pTo->z, pTo->n);
+	if (normalized_name == NULL) {
+		diag_set(OutOfMemory, pTo->n, "strndup", "normalized name");
+		goto fk_end;
+	}
+	sqlite3NormalizeName(normalized_name);
+	uint32_t parent_id = box_space_id_by_name(normalized_name,
+						  strlen(normalized_name));
+	if (parent_id == BOX_ID_NIL &&
+	    strcmp(normalized_name, p->def->name) != 0) {
+		diag_set(ClientError, ER_NO_SUCH_SPACE, normalized_name);
+		pParse->rc = SQL_TARANTOOL_ERROR;
+		pParse->nErr++;
+		goto fk_end;
+	}
+	struct space *parent_space = space_by_id(parent_id);
+	if (parent_space != NULL && parent_space->def->opts.is_view) {
+		sqlite3ErrorMsg(pParse, "referenced table can't be view");
+		goto fk_end;
+	}
 	if (p == 0)
 		goto fk_end;
 	if (pFromCol == 0) {
@@ -2138,8 +2164,8 @@ sqlite3CreateForeignKey(Parse * pParse,	/* Parsing context */
 	} else {
 		nCol = pFromCol->nExpr;
 	}
-	nByte =
-	    sizeof(*pFKey) + (nCol - 1) * sizeof(pFKey->aCol[0]) + pTo->n + 1;
+	nByte = sizeof(*pFKey) + (nCol - 1) * sizeof(pFKey->aCol[0]) +
+		strlen(normalized_name) + 1;
 	if (pToCol) {
 		for (i = 0; i < pToCol->nExpr; i++) {
 			nByte += sqlite3Strlen30(pToCol->a[i].zName) + 1;
@@ -2153,10 +2179,8 @@ sqlite3CreateForeignKey(Parse * pParse,	/* Parsing context */
 	pFKey->pNextFrom = p->pFKey;
 	z = (char *)&pFKey->aCol[nCol];
 	pFKey->zTo = z;
-	memcpy(z, pTo->z, pTo->n);
-	z[pTo->n] = 0;
-	sqlite3NormalizeName(z);
-	z += pTo->n + 1;
+	memcpy(z, normalized_name, strlen(normalized_name) + 1);
+	z += strlen(normalized_name) + 1;
 	pFKey->nCol = nCol;
 	if (pFromCol == 0) {
 		pFKey->aCol[0].iFrom = p->def->field_count - 1;
@@ -2210,6 +2234,7 @@ sqlite3CreateForeignKey(Parse * pParse,	/* Parsing context */
 
  fk_end:
 	sqlite3DbFree(db, pFKey);
+	free(normalized_name);
 #endif				/* !defined(SQLITE_OMIT_FOREIGN_KEY) */
 	sql_expr_list_delete(db, pFromCol);
 	sql_expr_list_delete(db, pToCol);
diff --git a/src/box/sql/fkey.c b/src/box/sql/fkey.c
index 6dd91306be..248bd31bfc 100644
--- a/src/box/sql/fkey.c
+++ b/src/box/sql/fkey.c
@@ -318,11 +318,9 @@ sqlite3FkLocateIndex(Parse * pParse,	/* Parse context to store any error in */
 	}
 
 	if (index == NULL) {
-		if (!pParse->disableTriggers) {
-			sqlite3ErrorMsg(pParse,
-					"foreign key mismatch - \"%w\" referencing \"%w\"",
-					pFKey->pFrom->def->name, pFKey->zTo);
-		}
+		sqlite3ErrorMsg(pParse, "foreign key mismatch - "
+					"\"%w\" referencing \"%w\"",
+				pFKey->pFrom->def->name, pFKey->zTo);
 		sqlite3DbFree(pParse->db, aiCol);
 		return 1;
 	}
@@ -736,46 +734,6 @@ sql_fk_trigger_delete(struct sqlite3 *db, struct sql_trigger *trigger)
 	sqlite3DbFree(db, trigger);
 }
 
-/**
- * This function is called to generate code that runs when table
- * pTab is being dropped from the database. The SrcList passed as
- * the second argument to this function contains a single entry
- * guaranteed to resolve to table pTab.
- *
- * Normally, no code is required. However, if the table is
- * parent table of a FK constraint, then the equivalent
- * of "DELETE FROM <tbl>" is executed in a single transaction
- * before dropping the table from the database. If any FK
- * violations occur, rollback transaction and halt VDBE. Triggers
- * are disabled while running this DELETE, but foreign key
- * actions are not.
- */
-void
-sqlite3FkDropTable(Parse *parser, SrcList *name, Table *table)
-{
-	struct session *user_session = current_session();
-	if ((user_session->sql_flags & SQLITE_ForeignKeys) == 0 ||
-	    table->def->opts.is_view || sqlite3FkReferences(table) == NULL)
-		return;
-	struct Vdbe *v = sqlite3GetVdbe(parser);
-	assert(v != NULL);
-	parser->disableTriggers = 1;
-	/* Staring new transaction before DELETE FROM <tbl> */
-	sqlite3VdbeAddOp0(v, OP_TTransaction);
-	sql_table_delete_from(parser, sqlite3SrcListDup(parser->db, name, 0),
-			      NULL);
-	parser->disableTriggers = 0;
-	/*
-	 * If the DELETE has generated immediate foreign key
-	 * constraint violations, rollback, halt the VDBE and
-	 * return an error at this point, before any modifications
-	 * of the _space and _index spaces. This is because these
-	 * spaces don't support multistatement transactions.
-	 * Otherwise, just commit changes.
-	 */
-	sqlite3VdbeAddOp0(v, OP_FkCheckCommit);
-}
-
 /*
  * The second argument points to an FKey object representing a foreign key
  * for which pTab is the child table. An UPDATE statement against pTab
@@ -884,7 +842,6 @@ sqlite3FkCheck(Parse * pParse,	/* Parse context */
 {
 	sqlite3 *db = pParse->db;	/* Database handle */
 	FKey *pFKey;		/* Used to iterate through FKs */
-	int isIgnoreErrors = pParse->disableTriggers;
 	struct session *user_session = current_session();
 
 	/* Exactly one of regOld and regNew should be non-zero. */
@@ -903,7 +860,6 @@ sqlite3FkCheck(Parse * pParse,	/* Parse context */
 		int *aiFree = 0;
 		int *aiCol;
 		int iCol;
-		int i;
 		int bIgnore = 0;
 
 		if (aChange
@@ -917,42 +873,10 @@ sqlite3FkCheck(Parse * pParse,	/* Parse context */
 		 * schema items cannot be located, set an error in pParse and return
 		 * early.
 		 */
-		if (pParse->disableTriggers) {
-			pTo = sqlite3HashFind(&db->pSchema->tblHash,
-					      pFKey->zTo);
-		} else {
-			pTo = sqlite3LocateTable(pParse, 0, pFKey->zTo);
-		}
-		if (!pTo
-		    || sqlite3FkLocateIndex(pParse, pTo, pFKey, &pIdx,
-					    &aiFree)) {
-			assert(isIgnoreErrors == 0
-			       || (regOld != 0 && regNew == 0));
-			if (!isIgnoreErrors || db->mallocFailed)
+		pTo = sqlite3LocateTable(pParse, 0, pFKey->zTo);
+		if (pTo == NULL || sqlite3FkLocateIndex(pParse, pTo, pFKey,
+							&pIdx, &aiFree) != 0)
 				return;
-			if (pTo == 0) {
-				/* If isIgnoreErrors is true, then a table is being dropped. In this
-				 * case SQLite runs a "DELETE FROM xxx" on the table being dropped
-				 * before actually dropping it in order to check FK constraints.
-				 * If the parent table of an FK constraint on the current table is
-				 * missing, behave as if it is empty. i.e. decrement the relevant
-				 * FK counter for each row of the current table with non-NULL keys.
-				 */
-				Vdbe *v = sqlite3GetVdbe(pParse);
-				int iJump =
-				    sqlite3VdbeCurrentAddr(v) + pFKey->nCol + 1;
-				for (i = 0; i < pFKey->nCol; i++) {
-					int iReg =
-					    pFKey->aCol[i].iFrom + regOld + 1;
-					sqlite3VdbeAddOp2(v, OP_IsNull, iReg,
-							  iJump);
-					VdbeCoverage(v);
-				}
-				sqlite3VdbeAddOp2(v, OP_FkCounter,
-						  pFKey->isDeferred, -1);
-			}
-			continue;
-		}
 		assert(pFKey->nCol == 1 || (aiFree && pIdx));
 
 		if (aiFree) {
@@ -1012,11 +936,9 @@ sqlite3FkCheck(Parse * pParse,	/* Parse context */
 			continue;
 		}
 
-		if (sqlite3FkLocateIndex(pParse, pTab, pFKey, &pIdx, &aiCol)) {
-			if (!isIgnoreErrors || db->mallocFailed)
-				return;
-			continue;
-		}
+		if (sqlite3FkLocateIndex(pParse, pTab, pFKey, &pIdx,
+					 &aiCol) != 0)
+			return;
 		assert(aiCol || pFKey->nCol == 1);
 
 		/* Create a SrcList structure containing the child table.  We need the
diff --git a/src/box/sql/sqliteInt.h b/src/box/sql/sqliteInt.h
index 73c33d9d6e..fd3c642039 100644
--- a/src/box/sql/sqliteInt.h
+++ b/src/box/sql/sqliteInt.h
@@ -2857,7 +2857,6 @@ struct Parse {
 	u32 newmask;		/* Mask of new.* columns referenced */
 	u8 eTriggerOp;		/* TK_UPDATE, TK_INSERT or TK_DELETE */
 	u8 eOrconf;		/* Default ON CONFLICT policy for trigger steps */
-	u8 disableTriggers;	/* True to disable triggers */
 	/** Region to make SQL temp allocations. */
 	struct region region;
 
@@ -4633,7 +4632,6 @@ void sqlite3WithPush(Parse *, With *, u8);
  */
 #if !defined(SQLITE_OMIT_FOREIGN_KEY)
 void sqlite3FkCheck(Parse *, Table *, int, int, int *);
-void sqlite3FkDropTable(Parse *, SrcList *, Table *);
 void sqlite3FkActions(Parse *, Table *, ExprList *, int, int *);
 int sqlite3FkRequired(Table *, int *);
 u32 sqlite3FkOldmask(Parse *, Table *);
@@ -4641,7 +4639,6 @@ FKey *sqlite3FkReferences(Table *);
 #else
 #define sqlite3FkActions(a,b,c,d,e)
 #define sqlite3FkCheck(a,b,c,d,e,f)
-#define sqlite3FkDropTable(a,b,c)
 #define sqlite3FkOldmask(a,b)         0
 #define sqlite3FkRequired(b,c)    0
 #endif
diff --git a/src/box/sql/vdbe.c b/src/box/sql/vdbe.c
index ca89908caa..192de1ae2c 100644
--- a/src/box/sql/vdbe.c
+++ b/src/box/sql/vdbe.c
@@ -2942,36 +2942,6 @@ case OP_Savepoint: {
 	break;
 }
 
-/* Opcode: FkCheckCommit * * * * *
- *
- * This opcode is used and required by DROP TABLE statement,
- * since deleted rows should be rollbacked in case of foreign keys
- * constraint violations. In case of rollback, instruction
- * also causes the VM to halt, because it makes no sense to continue
- * execution with FK violations. If there is no FK violations, then
- * just commit changes - deleted rows.
- *
- * Do not use this instruction in any statement implementation
- * except for DROP TABLE!
- */
-case OP_FkCheckCommit: {
-	if (!box_txn()) {
-		sqlite3VdbeError(p, "cannot commit or rollback - " \
-			"no transaction is active");
-		rc = SQLITE_ERROR;
-		goto abort_due_to_error;
-	}
-	if ((rc = sqlite3VdbeCheckFk(p, 0) != SQLITE_OK)) {
-		box_txn_rollback();
-		sqlite3VdbeHalt(p);
-		goto vdbe_return;
-	} else {
-		rc = box_txn_commit() == 0 ? SQLITE_OK : SQL_TARANTOOL_ERROR;
-		if (rc) goto abort_due_to_error;
-	}
-	break;
-}
-
 /* Opcode: CheckViewReferences P1 * * * *
  * Synopsis: r[P1] = space id
  *
diff --git a/test/sql-tap/alter.test.lua b/test/sql-tap/alter.test.lua
index cfe280121e..a1f6a24b43 100755
--- a/test/sql-tap/alter.test.lua
+++ b/test/sql-tap/alter.test.lua
@@ -313,9 +313,9 @@ test:do_execsql_test(
         DROP TABLE IF EXISTS t1;
         DROP TABLE IF EXISTS t2;
         DROP TABLE IF EXISTS t3;
+        CREATE TABLE t2(id INT PRIMARY KEY);
+        CREATE TABLE t3(id INT PRIMARY KEY);
         CREATE TABLE t1(a PRIMARY KEY, b, c, FOREIGN KEY(b) REFERENCES t2(id), FOREIGN KEY(c) REFERENCES t3(id));
-        CREATE TABLE t2(id PRIMARY KEY);
-        CREATE TABLE t3(id PRIMARY KEY);
         INSERT INTO t2 VALUES(1);
         INSERT INTO t3 VALUES(2);
         INSERT INTO t1 VALUES(1, 1, 2);
diff --git a/test/sql-tap/fkey1.test.lua b/test/sql-tap/fkey1.test.lua
index bca82d93dd..494af4b4a2 100755
--- a/test/sql-tap/fkey1.test.lua
+++ b/test/sql-tap/fkey1.test.lua
@@ -6,6 +6,15 @@ test:plan(19)
 
 test:do_execsql_test(
     "fkey1-1.1",
+    [[
+        CREATE TABLE t2(x PRIMARY KEY, y TEXT);
+    ]], {
+        -- <fkey1-1.1>
+        -- </fkey1-1.1>
+    })
+
+test:do_execsql_test(
+    "fkey1-1.2",
     [[
         CREATE TABLE t1(
             a INTEGER PRIMARY KEY,
@@ -19,15 +28,6 @@ test:do_execsql_test(
         -- </fkey1-1.1>
     })
 
-test:do_execsql_test(
-    "fkey1-1.2",
-    [[
-        CREATE TABLE t2(x PRIMARY KEY, y TEXT);
-    ]], {
-        -- <fkey1-1.2>
-        -- </fkey1-1.2>
-    })
-
 test:do_execsql_test(
     "fkey1-1.3",
     [[
diff --git a/test/sql-tap/fkey2.test.lua b/test/sql-tap/fkey2.test.lua
index 9d04a04b06..062597e9b1 100755
--- a/test/sql-tap/fkey2.test.lua
+++ b/test/sql-tap/fkey2.test.lua
@@ -1,6 +1,6 @@
 #!/usr/bin/env tarantool
 test = require("sqltester")
-test:plan(121)
+test:plan(117)
 
 -- This file implements regression tests for foreign keys.
 
@@ -15,9 +15,6 @@ test:do_execsql_test(
 
         CREATE TABLE t7(a, b INTEGER PRIMARY KEY);
         CREATE TABLE t8(c PRIMARY KEY REFERENCES t7, d);
-
-        CREATE TABLE t9(a PRIMARY KEY REFERENCES nosuchtable, b);
-        CREATE TABLE t10(a PRIMARY KEY REFERENCES t9(c), b);
     ]], {
         -- <fkey2-1.1>
         -- </fkey2-1.1>
@@ -301,21 +298,19 @@ test:do_catchsql_test(
 test:do_catchsql_test(
     "fkey2-1.29",
     [[
-        INSERT INTO t9 VALUES(1, 3);
+        CREATE TABLE t9(a PRIMARY KEY REFERENCES nosuchtable, b);
     ]], {
-        -- <fkey2-1.29>
-        1, "no such table: NOSUCHTABLE"
-        -- </fkey2-1.29>
+        1, "Space 'NOSUCHTABLE' does not exist"
     })
 
 test:do_catchsql_test(
     "fkey2-1.30",
     [[
-        INSERT INTO t10 VALUES(1, 3);
+        INSERT INTO t9 VALUES(1, 3);
     ]], {
-        -- <fkey2-1.30>
-        1, "foreign key mismatch - \"T10\" referencing \"T9\""
-        -- </fkey2-1.30>
+        -- <fkey2-1.29>
+        1, "no such table: T9"
+        -- </fkey2-1.29>
     })
 
 test:do_execsql_test(
@@ -729,14 +724,11 @@ test:do_catchsql_test(
 test:do_catchsql_test(
     "fkey2-7.2",
     [[
-        DROP TABLE IF EXISTS c;
-        DROP TABLE IF EXISTS p;
-        CREATE TABLE c(x PRIMARY KEY REFERENCES v(y));
-        CREATE VIEW v AS SELECT x AS y FROM c;
-        INSERT INTO c DEFAULT VALUES;
+        CREATE VIEW v AS SELECT b AS y FROM p;
+        CREATE TABLE d(x PRIMARY KEY REFERENCES v(y));
     ]], {
         -- <fkey2-7.2>
-        1, "foreign key mismatch - \"C\" referencing \"V\""
+        1, "referenced table can't be view"
         -- </fkey2-7.2>
     })
 
@@ -745,6 +737,7 @@ test:do_catchsql_test(
     [[
         DROP VIEW v;
         DROP TABLE IF EXISTS c;
+        DROP TABLE IF EXISTS p;
         CREATE TABLE p(a COLLATE binary, b PRIMARY KEY);
         CREATE UNIQUE INDEX idx ON p(a COLLATE "unicode_ci");
         CREATE TABLE c(x PRIMARY KEY REFERENCES p(a));
@@ -1050,15 +1043,15 @@ test:do_execsql_test(
 --         -- </fkey2-10.5>
 --     })
 
-test:do_execsql_test(
+test:do_catchsql_test(
     "fkey2-10.6",
     [[
         DROP TABLE IF EXISTS t2;
         DROP TABLE IF EXISTS t1;
         CREATE TABLE t1(a PRIMARY KEY, b REFERENCES nosuchtable);
-        DROP TABLE t1;
     ]], {
         -- <fkey2-10.6>
+        1, "Space 'NOSUCHTABLE' does not exist"
         -- </fkey2-10.6>
     })
 
@@ -1081,61 +1074,20 @@ test:do_catchsql_test(
         DROP TABLE t1;
     ]], {
         -- <fkey2-10.8>
-        1, "FOREIGN KEY constraint failed"
+        1, "Can't drop space 'T1': other objects depend on it"
         -- </fkey2-10.8>
     })
 
 test:do_execsql_test(
     "fkey2-10.9",
     [[
-        DELETE FROM t2;
+        DROP TABLE t2;
         DROP TABLE t1;
     ]], {
         -- <fkey2-10.9>
         -- </fkey2-10.9>
     })
 
-test:do_catchsql_test(
-    "fkey2-10.10",
-    [[
-        INSERT INTO t2 VALUES('x');
-    ]], {
-        -- <fkey2-10.10>
-        1, "no such table: T1"
-        -- </fkey2-10.10>
-    })
-
-test:do_execsql_test(
-    "fkey2-10.11",
-    [[
-        CREATE TABLE t1(x PRIMARY KEY);
-        INSERT INTO t1 VALUES('x');
-        INSERT INTO t2 VALUES('x');
-    ]], {
-        -- <fkey2-10.11>
-        -- </fkey2-10.11>
-    })
-
-test:do_catchsql_test(
-    "fkey2-10.12",
-    [[
-        DROP TABLE t1;
-    ]], {
-        -- <fkey2-10.12>
-        1, "FOREIGN KEY constraint failed"
-        -- </fkey2-10.12>
-    })
-
-test:do_execsql_test(
-    "fkey2-10.13",
-    [[
-        DROP TABLE t2;
-        DROP TABLE t1;
-    ]], {
-        -- <fkey2-10.13>
-        -- </fkey2-10.13>
-    })
-
 test:do_execsql_test(
     "fkey2-10.14",
     [[
@@ -1186,7 +1138,7 @@ test:do_execsql_test(
         -- </fkey2-10.17>
     })
 
-test:do_execsql_test(
+test:do_catchsql_test(
     "fkey2-10.18",
     [[
         CREATE TABLE b1(a PRIMARY KEY, b);
@@ -1194,28 +1146,30 @@ test:do_execsql_test(
         DROP TABLE b1;
     ]], {
         -- <fkey2-10.18>
+        1, "Can't drop space 'B1': other objects depend on it"
         -- </fkey2-10.18>
     })
 
-test:do_execsql_test(
+test:do_catchsql_test(
     "fkey2-10.19",
     [[
         CREATE TABLE b3(a PRIMARY KEY, b REFERENCES b2 DEFERRABLE INITIALLY DEFERRED);
         DROP TABLE b2;
     ]], {
         -- <fkey2-10.19>
+        1, "Can't drop space 'B2': other objects depend on it"
         -- </fkey2-10.19>
     })
 
-test:do_execsql_test(
+test:do_catchsql_test(
     "fkey2-10.20",
     [[
         DROP VIEW IF EXISTS v;
+        CREATE VIEW v AS SELECT * FROM b1;
         CREATE TABLE t1(x PRIMARY KEY REFERENCES v);
-        CREATE VIEW v AS SELECT * FROM t1;
-        DROP VIEW v;
     ]], {
         -- <fkey2-10.20>
+        1, "referenced table can't be view"
         -- </fkey2-10.20>
     })
 
diff --git a/test/sql-tap/fkey3.test.lua b/test/sql-tap/fkey3.test.lua
index 82796ba337..d7055b0965 100755
--- a/test/sql-tap/fkey3.test.lua
+++ b/test/sql-tap/fkey3.test.lua
@@ -36,7 +36,7 @@ test:do_catchsql_test(
         DROP TABLE t1;
     ]], {
         -- <fkey3-1.3.1>
-        1, "FOREIGN KEY constraint failed"
+        1, "Can't drop space 'T1': other objects depend on it"
         -- </fkey3-1.3.1>
     })
 
@@ -46,7 +46,7 @@ test:do_catchsql_test(
         DROP TABLE t1;
     ]], {
         -- <fkey3-1.3.2>
-        1, "FOREIGN KEY constraint failed"
+        1, "Can't drop space 'T1': other objects depend on it"
         -- </fkey3-1.3.2>
     })
 
diff --git a/test/sql-tap/gh-2953-drop-table-with-FK.test.lua b/test/sql-tap/gh-2953-drop-table-with-FK.test.lua
deleted file mode 100755
index 43e541d788..0000000000
--- a/test/sql-tap/gh-2953-drop-table-with-FK.test.lua
+++ /dev/null
@@ -1,122 +0,0 @@
-#!/usr/bin/env tarantool
-
-test = require("sqltester")
-test:plan(11)
-
-test:do_execsql_test(
-	"drop-table-fk-1.1",
-	[[
-		DROP TABLE IF EXISTS t3;
-		DROP TABLE IF EXISTS t2;
-		CREATE TABLE t2(id PRIMARY KEY);
-		CREATE TABLE t3(id PRIMARY KEY REFERENCES t2(id));
-		INSERT INTO t2 VALUES(1), (2), (3);
-		INSERT INTO t3 VALUES(3);
-	]], {
-		-- <drop-table-fk-1.1>
-		-- <drop-table-fk-1.1>
-	})
-
-test:do_catchsql_test(
-	"drop-table-fk-1.2",
-	[[
-		DROP TABLE t2;
-	]], {
-		-- <drop-table-fk-1.2>
-		1, "FOREIGN KEY constraint failed"
-		-- <drop-table-fk-1.2>
-	})
-
-test:do_catchsql_test(
-	"drop-table-fk-1.3",
-	[[
-		DROP TABLE t2;
-	]], {
-		-- <drop-table-fk-1.3>
-		1, "FOREIGN KEY constraint failed"
-		-- <drop-table-fk-1.3>
-	})
-
-test:do_execsql_test(
-	"drop-table-fk-1.4",
-	[[
-		SELECT * FROM t2;
-	]], {
-		-- <drop-table-fk-1.4>
-		1, 2, 3
-		-- <drop-table-fk-1.4>
-	})
-
-test:do_catchsql_test(
-	"drop-table-fk-1.5",
-	[[
-		DELETE FROM t2 WHERE id = 3;
-	]], {
-		-- <drop-table-fk-1.5>
-		1, "FOREIGN KEY constraint failed"
-		-- <drop-table-fk-1.5>
-	})
-
-test:do_execsql_test(
-	"drop-table-fk-1.6",
-	[[
-		SELECT * FROM t2;
-	]], {
-		-- <drop-table-fk-1.6>
-		1, 2, 3
-		-- <drop-table-fk-1.6>
-	})
-
-test:do_catchsql_test(
-	"drop-table-fk-1.7",
-	[[
-		DELETE FROM t2;
-	]], {
-		-- <drop-table-fk-1.7>
-		1, "FOREIGN KEY constraint failed"
-		-- <drop-table-fk-1.7>
-	})
-
-test:do_execsql_test(
-	"drop-table-fk-1.8",
-	[[
-		SELECT * FROM t2;
-	]], {
-		-- <drop-table-fk-1.8>
-		1, 2, 3
-		-- <drop-table-fk-1.8>
-	})
-
-test:do_execsql_test(
-	"drop-table-fk-1.9",
-	[[
-		DROP TABLE t3;
-		DROP TABLE t2;
-	]], {
-		-- <drop-table-fk-1.9>
-		-- <drop-table-fk-1.9>
-	})
-
-test:do_execsql_test(
-	"drop-table-fk-2.1",
-	[[
-		CREATE TABLE t2(id PRIMARY KEY);
-		CREATE TABLE t3(id PRIMARY KEY REFERENCES t2(id) ON DELETE CASCADE);
-		INSERT INTO t2 VALUES(1), (2), (3);
-		INSERT INTO t3 VALUES(3);
-	]], {
-		-- <drop-table-fk-2.1>
-		-- <drop-table-fk-2.1>
-	})
-
-test:do_execsql_test(
-	"drop-table-fk-2.2",
-	[[
-		DROP TABLE t2;
-		SELECT * FROM t3;
-	]], {
-		-- <drop-table-fk-2.2>
-		-- <drop-table-fk-2.2>
-	})
-
-test:finish_test()
diff --git a/test/sql-tap/table.test.lua b/test/sql-tap/table.test.lua
index c521b309c5..3f8182fc46 100755
--- a/test/sql-tap/table.test.lua
+++ b/test/sql-tap/table.test.lua
@@ -730,6 +730,7 @@ test:do_catchsql_test(
     "table-10.2",
     [[
         DROP TABLE t6;
+        CREATE TABLE t4(a INT PRIMARY KEY);
         CREATE TABLE t6(a REFERENCES t4(a) MATCH PARTIAL primary key);
     ]], {
         -- <table-10.2>
diff --git a/test/sql-tap/tkt-b1d3a2e531.test.lua b/test/sql-tap/tkt-b1d3a2e531.test.lua
index d5f34ccfc4..e140cf82a1 100755
--- a/test/sql-tap/tkt-b1d3a2e531.test.lua
+++ b/test/sql-tap/tkt-b1d3a2e531.test.lua
@@ -124,7 +124,7 @@ test:do_catchsql_test(
           DROP TABLE cc1;
     ]], {
         -- <3.2>
-        1, "FOREIGN KEY constraint failed"
+        1, "Can't drop space 'PP1': other objects depend on it"
         -- </3.2>
     })
 
-- 
GitLab