Unverified 提交 b94d69d2 authored 作者: Noel Grandin's avatar Noel Grandin 提交者: GitHub

Merge pull request #720 from ilm-informatique/dropTableRestrict

DROP TABLE RESTRICT shouldn't drop foreign keys in other tables
......@@ -907,8 +907,8 @@ DROP SEQUENCE SEQ_ID
DROP TABLE [ IF EXISTS ] tableName [,...] [ RESTRICT | CASCADE ]
","
Drops an existing table, or a list of tables.
The command will fail if dependent views exist and the RESTRICT clause is used (the default).
All dependent views are dropped as well if the CASCADE clause is used.
The command will fail if dependent objects exist and the RESTRICT clause is used (the default).
All dependent views and constraints are dropped as well if the CASCADE clause is used.
This command commits an open transaction in this connection.
","
DROP TABLE TEST
......
......@@ -5,9 +5,12 @@
*/
package org.h2.command.ddl;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import org.h2.api.ErrorCode;
import org.h2.command.CommandInterface;
import org.h2.constraint.Constraint;
import org.h2.constraint.ConstraintReferential;
import org.h2.engine.Database;
import org.h2.engine.Right;
......@@ -73,15 +76,29 @@ public class DropTable extends SchemaCommand {
throw DbException.get(ErrorCode.CANNOT_DROP_TABLE_1, tableName);
}
if (dropAction == ConstraintReferential.RESTRICT) {
StatementBuilder buff = new StatementBuilder();
CopyOnWriteArrayList<TableView> dependentViews = table.getDependentViews();
if (dependentViews != null && dependentViews.size() > 0) {
StatementBuilder buff = new StatementBuilder();
for (TableView v : dependentViews) {
buff.appendExceptFirst(", ");
buff.append(v.getName());
}
throw DbException.get(ErrorCode.CANNOT_DROP_2, tableName, buff.toString());
}
if (session.getDatabase()
.getSettings().standardDropTableRestrict) {
final List<Constraint> constraints = table.getConstraints();
if (constraints != null && constraints.size() > 0) {
for (Constraint c : constraints) {
if (c.getTable() != table) {
buff.appendExceptFirst(", ");
buff.append(c.getName());
}
}
}
}
if (buff.length() > 0)
throw DbException.get(ErrorCode.CANNOT_DROP_2, tableName, buff.toString());
}
table.lock(session, true, true);
}
......
......@@ -350,6 +350,17 @@ public class DbSettings extends SettingsBase {
*/
public final boolean multiThreaded = get("MULTI_THREADED", false);
/**
* Database setting <code>STANDARD_DROP_TABLE_RESTRICT</code> (default:
* false).<br />
* <code>true</code> if DROP TABLE RESTRICT should fail if there's any
* foreign key referencing the table to be dropped. <code>false</code> if
* foreign keys referencing the table to be dropped should be silently
* dropped as well.
*/
public final boolean standardDropTableRestrict = get(
"STANDARD_DROP_TABLE_RESTRICT", false);
private DbSettings(HashMap<String, String> s) {
super(s);
}
......
......@@ -19,7 +19,6 @@ import java.sql.Timestamp;
import java.util.List;
import java.util.Random;
import java.util.concurrent.TimeUnit;
import org.h2.api.ErrorCode;
import org.h2.engine.Constants;
import org.h2.store.fs.FileUtils;
......@@ -54,6 +53,7 @@ public class TestCases extends TestBase {
testGroupSubquery();
testCountDistinctNotNull();
testDependencies();
testDropTable();
testConvertType();
testSortedSelect();
testMaxMemoryRows();
......@@ -150,7 +150,7 @@ public class TestCases extends TestBase {
stat.execute("alter table b add constraint x " +
"foreign key(a_id) references a(id)");
stat.execute("update a set x=200");
stat.execute("drop table if exists a, b");
stat.execute("drop table if exists a, b cascade");
conn.close();
}
......@@ -278,6 +278,70 @@ public class TestCases extends TestBase {
conn.close();
}
private void testDropTable() throws SQLException {
trace("testDropTable");
final boolean[] booleans = new boolean[] { true, false };
for (final boolean stdDropTableRestrict : booleans) {
for (final boolean restrict : booleans) {
testDropTableNoReference(stdDropTableRestrict, restrict);
testDropTableViewReference(stdDropTableRestrict, restrict);
testDropTableForeignKeyReference(stdDropTableRestrict, restrict);
}
}
}
private Statement createTable(final boolean stdDropTableRestrict) throws SQLException {
deleteDb("cases");
Connection conn = getConnection("cases;STANDARD_DROP_TABLE_RESTRICT=" + stdDropTableRestrict);
Statement stat = conn.createStatement();
stat.execute("create table test(id int)");
return stat;
}
private void dropTable(final boolean restrict, Statement stat, final boolean expectedDropSuccess)
throws SQLException {
assertThrows(expectedDropSuccess ? 0 : ErrorCode.CANNOT_DROP_2, stat)
.execute("drop table test " + (restrict ? "restrict" : "cascade"));
assertThrows(expectedDropSuccess ? ErrorCode.TABLE_OR_VIEW_NOT_FOUND_1 : 0, stat).execute("select * from test");
}
private void testDropTableNoReference(final boolean stdDropTableRestrict, final boolean restrict)
throws SQLException {
Statement stat = createTable(stdDropTableRestrict);
// always succeed as there's no reference to the table
dropTable(restrict, stat, true);
stat.getConnection().close();
}
private void testDropTableViewReference(final boolean stdDropTableRestrict, final boolean restrict)
throws SQLException {
Statement stat = createTable(stdDropTableRestrict);
stat.execute("create view abc as select * from test");
// drop allowed only if cascade
final boolean expectedDropSuccess = !restrict;
dropTable(restrict, stat, expectedDropSuccess);
// missing view if the drop succeeded
assertThrows(expectedDropSuccess ? ErrorCode.TABLE_OR_VIEW_NOT_FOUND_1 : 0, stat).execute("select * from abc");
stat.getConnection().close();
}
private void testDropTableForeignKeyReference(final boolean stdDropTableRestrict, final boolean restrict)
throws SQLException {
Statement stat = createTable(stdDropTableRestrict);
stat.execute("create table ref(id int, id_test int, foreign key (id_test) references test (id)) ");
// test table is empty, so the foreign key forces ref table to be also
// empty
assertThrows(ErrorCode.REFERENTIAL_INTEGRITY_VIOLATED_PARENT_MISSING_1, stat)
.execute("insert into ref values(1,2)");
// drop allowed if cascade or old style
final boolean expectedDropSuccess = !stdDropTableRestrict || !restrict;
dropTable(restrict, stat, expectedDropSuccess);
// insertion succeeds if the foreign key was dropped
assertThrows(expectedDropSuccess ? 0 : ErrorCode.REFERENTIAL_INTEGRITY_VIOLATED_PARENT_MISSING_1, stat)
.execute("insert into ref values(1,2)");
stat.getConnection().close();
}
private void testConvertType() throws SQLException {
deleteDb("cases");
Connection conn = getConnection("cases");
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论