summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJenkins <jenkins@review.openstack.org>2015-04-10 13:58:09 +0000
committerGerrit Code Review <review@openstack.org>2015-04-10 13:58:09 +0000
commitebb3f0e2b5abd473d14404d43b0f0c32c2a4d5e2 (patch)
tree0a0e5f08c4b09e2e9433badf5c202eca58fe4a3c
parent9c852c63cd153aaa820d9bf5ebda7b5b7e1265c3 (diff)
parentf94046bf20b315e151d668b55d737ae41414e421 (diff)
downloadoslo-db-ebb3f0e2b5abd473d14404d43b0f0c32c2a4d5e2.tar.gz
Merge "exc_filters: support for ForeignKey error on delete"
-rw-r--r--oslo_db/sqlalchemy/exc_filters.py6
-rw-r--r--oslo_db/tests/sqlalchemy/test_exc_filters.py79
2 files changed, 80 insertions, 5 deletions
diff --git a/oslo_db/sqlalchemy/exc_filters.py b/oslo_db/sqlalchemy/exc_filters.py
index 605b8a5..af8d992 100644
--- a/oslo_db/sqlalchemy/exc_filters.py
+++ b/oslo_db/sqlalchemy/exc_filters.py
@@ -191,12 +191,12 @@ def _sqlite_dupe_key_error(integrity_error, match, engine_name, is_disconnect):
r"(?i).*foreign key constraint failed")
@filters("postgresql", sqla_exc.IntegrityError,
r".*on table \"(?P<table>[^\"]+)\" violates "
- "foreign key constraint \"(?P<constraint>[^\"]+)\"\s*\n"
+ "foreign key constraint \"(?P<constraint>[^\"]+)\".*\n"
"DETAIL: Key \((?P<key>.+)\)=\(.+\) "
- "is not present in table "
+ "is (not present in|still referenced from) table "
"\"(?P<key_table>[^\"]+)\".")
@filters("mysql", sqla_exc.IntegrityError,
- r".* u?'Cannot add or update a child row: "
+ r".* u?'Cannot (add|delete) or update a (child|parent) row: "
'a foreign key constraint fails \([`"].+[`"]\.[`"](?P<table>.+)[`"], '
'CONSTRAINT [`"](?P<constraint>.+)[`"] FOREIGN KEY '
'\([`"](?P<key>.+)[`"]\) REFERENCES [`"](?P<key_table>.+)[`"] ')
diff --git a/oslo_db/tests/sqlalchemy/test_exc_filters.py b/oslo_db/tests/sqlalchemy/test_exc_filters.py
index 3f4f277..dc5de84 100644
--- a/oslo_db/tests/sqlalchemy/test_exc_filters.py
+++ b/oslo_db/tests/sqlalchemy/test_exc_filters.py
@@ -233,14 +233,14 @@ class TestReferenceErrorSQLite(_SQLAExceptionMatcher, test_base.DbTestCase):
meta = sqla.MetaData(bind=self.engine)
- table_1 = sqla.Table(
+ self.table_1 = sqla.Table(
"resource_foo", meta,
sqla.Column("id", sqla.Integer, primary_key=True),
sqla.Column("foo", sqla.Integer),
mysql_engine='InnoDB',
mysql_charset='utf8',
)
- table_1.create()
+ self.table_1.create()
self.table_2 = sqla.Table(
"resource_entity", meta,
@@ -274,6 +274,30 @@ class TestReferenceErrorSQLite(_SQLAExceptionMatcher, test_base.DbTestCase):
self.assertIsNone(matched.key)
self.assertIsNone(matched.key_table)
+ def test_raise_delete(self):
+ self.engine.execute("PRAGMA foreign_keys = ON;")
+
+ with self.engine.connect() as conn:
+ conn.execute(self.table_1.insert({"id": 1234, "foo": 42}))
+ conn.execute(self.table_2.insert({"id": 4321, "foo_id": 1234}))
+ matched = self.assertRaises(
+ exception.DBReferenceError,
+ self.engine.execute,
+ self.table_1.delete()
+ )
+ self.assertInnerException(
+ matched,
+ "IntegrityError",
+ "foreign key constraint failed",
+ "DELETE FROM resource_foo",
+ (),
+ )
+
+ self.assertIsNone(matched.table)
+ self.assertIsNone(matched.constraint)
+ self.assertIsNone(matched.key)
+ self.assertIsNone(matched.key_table)
+
class TestReferenceErrorPostgreSQL(TestReferenceErrorSQLite,
test_base.PostgreSQLOpportunisticTestCase):
@@ -300,6 +324,31 @@ class TestReferenceErrorPostgreSQL(TestReferenceErrorSQLite,
self.assertEqual("foo_id", matched.key)
self.assertEqual("resource_foo", matched.key_table)
+ def test_raise_delete(self):
+ with self.engine.connect() as conn:
+ conn.execute(self.table_1.insert({"id": 1234, "foo": 42}))
+ conn.execute(self.table_2.insert({"id": 4321, "foo_id": 1234}))
+ matched = self.assertRaises(
+ exception.DBReferenceError,
+ self.engine.execute,
+ self.table_1.delete()
+ )
+ self.assertInnerException(
+ matched,
+ "IntegrityError",
+ "update or delete on table \"resource_foo\" violates foreign key "
+ "constraint \"foo_fkey\" on table \"resource_entity\"\n"
+ "DETAIL: Key (id)=(1234) is still referenced from "
+ "table \"resource_entity\".\n",
+ "DELETE FROM resource_foo",
+ {},
+ )
+
+ self.assertEqual("resource_foo", matched.table)
+ self.assertEqual("foo_fkey", matched.constraint)
+ self.assertEqual("id", matched.key)
+ self.assertEqual("resource_entity", matched.key_table)
+
class TestReferenceErrorMySQL(TestReferenceErrorSQLite,
test_base.MySQLOpportunisticTestCase):
@@ -353,6 +402,32 @@ class TestReferenceErrorMySQL(TestReferenceErrorSQLite,
self.assertEqual("foo_id", matched.key)
self.assertEqual("resource_foo", matched.key_table)
+ def test_raise_delete(self):
+ with self.engine.connect() as conn:
+ conn.execute(self.table_1.insert({"id": 1234, "foo": 42}))
+ conn.execute(self.table_2.insert({"id": 4321, "foo_id": 1234}))
+ matched = self.assertRaises(
+ exception.DBReferenceError,
+ self.engine.execute,
+ self.table_1.delete()
+ )
+ self.assertInnerException(
+ matched,
+ "IntegrityError",
+ "(1451, 'cannot delete or update a parent row: a foreign key "
+ "constraint fails (`{0}`.`resource_entity`, "
+ "constraint `foo_fkey` "
+ "foreign key (`foo_id`) references "
+ "`resource_foo` (`id`))')".format(self.engine.url.database),
+ "DELETE FROM resource_foo",
+ (),
+ )
+
+ self.assertEqual("resource_entity", matched.table)
+ self.assertEqual("foo_fkey", matched.constraint)
+ self.assertEqual("foo_id", matched.key)
+ self.assertEqual("resource_foo", matched.key_table)
+
class TestConstraint(TestsExceptionFilter):
def test_postgresql(self):