Refs #29928 -- Implemented fast constraint checking on SQLite 3.20+.
This commit is contained in:
parent
ae2897aaf8
commit
a939d630a4
|
@ -8,6 +8,7 @@ import math
|
||||||
import operator
|
import operator
|
||||||
import re
|
import re
|
||||||
import warnings
|
import warnings
|
||||||
|
from itertools import chain
|
||||||
from sqlite3 import dbapi2 as Database
|
from sqlite3 import dbapi2 as Database
|
||||||
|
|
||||||
import pytz
|
import pytz
|
||||||
|
@ -266,37 +267,68 @@ class DatabaseWrapper(BaseDatabaseWrapper):
|
||||||
determine if rows with invalid references were entered while constraint
|
determine if rows with invalid references were entered while constraint
|
||||||
checks were off.
|
checks were off.
|
||||||
"""
|
"""
|
||||||
with self.cursor() as cursor:
|
if Database.sqlite_version_info >= (3, 20, 0):
|
||||||
if table_names is None:
|
with self.cursor() as cursor:
|
||||||
table_names = self.introspection.table_names(cursor)
|
if table_names is None:
|
||||||
for table_name in table_names:
|
violations = self.cursor().execute('PRAGMA foreign_key_check').fetchall()
|
||||||
primary_key_column_name = self.introspection.get_primary_key_column(cursor, table_name)
|
else:
|
||||||
if not primary_key_column_name:
|
violations = chain.from_iterable(
|
||||||
continue
|
cursor.execute('PRAGMA foreign_key_check(%s)' % table_name).fetchall()
|
||||||
key_columns = self.introspection.get_key_columns(cursor, table_name)
|
for table_name in table_names
|
||||||
for column_name, referenced_table_name, referenced_column_name in key_columns:
|
)
|
||||||
cursor.execute(
|
# See https://www.sqlite.org/pragma.html#pragma_foreign_key_check
|
||||||
"""
|
for table_name, rowid, referenced_table_name, foreign_key_index in violations:
|
||||||
SELECT REFERRING.`%s`, REFERRING.`%s` FROM `%s` as REFERRING
|
foreign_key = cursor.execute(
|
||||||
LEFT JOIN `%s` as REFERRED
|
'PRAGMA foreign_key_list(%s)' % table_name
|
||||||
ON (REFERRING.`%s` = REFERRED.`%s`)
|
).fetchall()[foreign_key_index]
|
||||||
WHERE REFERRING.`%s` IS NOT NULL AND REFERRED.`%s` IS NULL
|
column_name, referenced_column_name = foreign_key[3:5]
|
||||||
"""
|
primary_key_column_name = self.introspection.get_primary_key_column(cursor, table_name)
|
||||||
% (
|
primary_key_value, bad_value = cursor.execute(
|
||||||
primary_key_column_name, column_name, table_name,
|
'SELECT %s, %s FROM %s WHERE rowid = %%s' % (
|
||||||
referenced_table_name, column_name, referenced_column_name,
|
primary_key_column_name, column_name, table_name
|
||||||
column_name, referenced_column_name,
|
),
|
||||||
|
(rowid,),
|
||||||
|
).fetchone()
|
||||||
|
raise utils.IntegrityError(
|
||||||
|
"The row in table '%s' with primary key '%s' has an "
|
||||||
|
"invalid foreign key: %s.%s contains a value '%s' that "
|
||||||
|
"does not have a corresponding value in %s.%s." % (
|
||||||
|
table_name, primary_key_value, table_name, column_name,
|
||||||
|
bad_value, referenced_table_name, referenced_column_name
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
for bad_row in cursor.fetchall():
|
else:
|
||||||
raise utils.IntegrityError(
|
with self.cursor() as cursor:
|
||||||
"The row in table '%s' with primary key '%s' has an "
|
if table_names is None:
|
||||||
"invalid foreign key: %s.%s contains a value '%s' that "
|
table_names = self.introspection.table_names(cursor)
|
||||||
"does not have a corresponding value in %s.%s." % (
|
for table_name in table_names:
|
||||||
table_name, bad_row[0], table_name, column_name,
|
primary_key_column_name = self.introspection.get_primary_key_column(cursor, table_name)
|
||||||
bad_row[1], referenced_table_name, referenced_column_name,
|
if not primary_key_column_name:
|
||||||
|
continue
|
||||||
|
key_columns = self.introspection.get_key_columns(cursor, table_name)
|
||||||
|
for column_name, referenced_table_name, referenced_column_name in key_columns:
|
||||||
|
cursor.execute(
|
||||||
|
"""
|
||||||
|
SELECT REFERRING.`%s`, REFERRING.`%s` FROM `%s` as REFERRING
|
||||||
|
LEFT JOIN `%s` as REFERRED
|
||||||
|
ON (REFERRING.`%s` = REFERRED.`%s`)
|
||||||
|
WHERE REFERRING.`%s` IS NOT NULL AND REFERRED.`%s` IS NULL
|
||||||
|
"""
|
||||||
|
% (
|
||||||
|
primary_key_column_name, column_name, table_name,
|
||||||
|
referenced_table_name, column_name, referenced_column_name,
|
||||||
|
column_name, referenced_column_name,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
for bad_row in cursor.fetchall():
|
||||||
|
raise utils.IntegrityError(
|
||||||
|
"The row in table '%s' with primary key '%s' has an "
|
||||||
|
"invalid foreign key: %s.%s contains a value '%s' that "
|
||||||
|
"does not have a corresponding value in %s.%s." % (
|
||||||
|
table_name, bad_row[0], table_name, column_name,
|
||||||
|
bad_row[1], referenced_table_name, referenced_column_name,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
def is_usable(self):
|
def is_usable(self):
|
||||||
return True
|
return True
|
||||||
|
|
Loading…
Reference in New Issue