summaryrefslogtreecommitdiff
path: root/django/db/backends/sqlite3/base.py
diff options
context:
space:
mode:
Diffstat (limited to 'django/db/backends/sqlite3/base.py')
-rw-r--r--django/db/backends/sqlite3/base.py224
1 files changed, 128 insertions, 96 deletions
diff --git a/django/db/backends/sqlite3/base.py b/django/db/backends/sqlite3/base.py
index 4343ea180e..5bcd61eb96 100644
--- a/django/db/backends/sqlite3/base.py
+++ b/django/db/backends/sqlite3/base.py
@@ -32,13 +32,13 @@ def decoder(conv_func):
def check_sqlite_version():
if Database.sqlite_version_info < (3, 9, 0):
raise ImproperlyConfigured(
- 'SQLite 3.9.0 or later is required (found %s).' % Database.sqlite_version
+ "SQLite 3.9.0 or later is required (found %s)." % Database.sqlite_version
)
check_sqlite_version()
-Database.register_converter("bool", b'1'.__eq__)
+Database.register_converter("bool", b"1".__eq__)
Database.register_converter("time", decoder(parse_time))
Database.register_converter("datetime", decoder(parse_datetime))
Database.register_converter("timestamp", decoder(parse_datetime))
@@ -47,69 +47,69 @@ Database.register_adapter(decimal.Decimal, str)
class DatabaseWrapper(BaseDatabaseWrapper):
- vendor = 'sqlite'
- display_name = 'SQLite'
+ vendor = "sqlite"
+ display_name = "SQLite"
# SQLite doesn't actually support most of these types, but it "does the right
# thing" given more verbose field definitions, so leave them as is so that
# schema inspection is more useful.
data_types = {
- 'AutoField': 'integer',
- 'BigAutoField': 'integer',
- 'BinaryField': 'BLOB',
- 'BooleanField': 'bool',
- 'CharField': 'varchar(%(max_length)s)',
- 'DateField': 'date',
- 'DateTimeField': 'datetime',
- 'DecimalField': 'decimal',
- 'DurationField': 'bigint',
- 'FileField': 'varchar(%(max_length)s)',
- 'FilePathField': 'varchar(%(max_length)s)',
- 'FloatField': 'real',
- 'IntegerField': 'integer',
- 'BigIntegerField': 'bigint',
- 'IPAddressField': 'char(15)',
- 'GenericIPAddressField': 'char(39)',
- 'JSONField': 'text',
- 'OneToOneField': 'integer',
- 'PositiveBigIntegerField': 'bigint unsigned',
- 'PositiveIntegerField': 'integer unsigned',
- 'PositiveSmallIntegerField': 'smallint unsigned',
- 'SlugField': 'varchar(%(max_length)s)',
- 'SmallAutoField': 'integer',
- 'SmallIntegerField': 'smallint',
- 'TextField': 'text',
- 'TimeField': 'time',
- 'UUIDField': 'char(32)',
+ "AutoField": "integer",
+ "BigAutoField": "integer",
+ "BinaryField": "BLOB",
+ "BooleanField": "bool",
+ "CharField": "varchar(%(max_length)s)",
+ "DateField": "date",
+ "DateTimeField": "datetime",
+ "DecimalField": "decimal",
+ "DurationField": "bigint",
+ "FileField": "varchar(%(max_length)s)",
+ "FilePathField": "varchar(%(max_length)s)",
+ "FloatField": "real",
+ "IntegerField": "integer",
+ "BigIntegerField": "bigint",
+ "IPAddressField": "char(15)",
+ "GenericIPAddressField": "char(39)",
+ "JSONField": "text",
+ "OneToOneField": "integer",
+ "PositiveBigIntegerField": "bigint unsigned",
+ "PositiveIntegerField": "integer unsigned",
+ "PositiveSmallIntegerField": "smallint unsigned",
+ "SlugField": "varchar(%(max_length)s)",
+ "SmallAutoField": "integer",
+ "SmallIntegerField": "smallint",
+ "TextField": "text",
+ "TimeField": "time",
+ "UUIDField": "char(32)",
}
data_type_check_constraints = {
- 'PositiveBigIntegerField': '"%(column)s" >= 0',
- 'JSONField': '(JSON_VALID("%(column)s") OR "%(column)s" IS NULL)',
- 'PositiveIntegerField': '"%(column)s" >= 0',
- 'PositiveSmallIntegerField': '"%(column)s" >= 0',
+ "PositiveBigIntegerField": '"%(column)s" >= 0',
+ "JSONField": '(JSON_VALID("%(column)s") OR "%(column)s" IS NULL)',
+ "PositiveIntegerField": '"%(column)s" >= 0',
+ "PositiveSmallIntegerField": '"%(column)s" >= 0',
}
data_types_suffix = {
- 'AutoField': 'AUTOINCREMENT',
- 'BigAutoField': 'AUTOINCREMENT',
- 'SmallAutoField': 'AUTOINCREMENT',
+ "AutoField": "AUTOINCREMENT",
+ "BigAutoField": "AUTOINCREMENT",
+ "SmallAutoField": "AUTOINCREMENT",
}
# SQLite requires LIKE statements to include an ESCAPE clause if the value
# being escaped has a percent or underscore in it.
# See https://www.sqlite.org/lang_expr.html for an explanation.
operators = {
- 'exact': '= %s',
- 'iexact': "LIKE %s ESCAPE '\\'",
- 'contains': "LIKE %s ESCAPE '\\'",
- 'icontains': "LIKE %s ESCAPE '\\'",
- 'regex': 'REGEXP %s',
- 'iregex': "REGEXP '(?i)' || %s",
- 'gt': '> %s',
- 'gte': '>= %s',
- 'lt': '< %s',
- 'lte': '<= %s',
- 'startswith': "LIKE %s ESCAPE '\\'",
- 'endswith': "LIKE %s ESCAPE '\\'",
- 'istartswith': "LIKE %s ESCAPE '\\'",
- 'iendswith': "LIKE %s ESCAPE '\\'",
+ "exact": "= %s",
+ "iexact": "LIKE %s ESCAPE '\\'",
+ "contains": "LIKE %s ESCAPE '\\'",
+ "icontains": "LIKE %s ESCAPE '\\'",
+ "regex": "REGEXP %s",
+ "iregex": "REGEXP '(?i)' || %s",
+ "gt": "> %s",
+ "gte": ">= %s",
+ "lt": "< %s",
+ "lte": "<= %s",
+ "startswith": "LIKE %s ESCAPE '\\'",
+ "endswith": "LIKE %s ESCAPE '\\'",
+ "istartswith": "LIKE %s ESCAPE '\\'",
+ "iendswith": "LIKE %s ESCAPE '\\'",
}
# The patterns below are used to generate SQL pattern lookup clauses when
@@ -122,12 +122,12 @@ class DatabaseWrapper(BaseDatabaseWrapper):
# the LIKE operator.
pattern_esc = r"REPLACE(REPLACE(REPLACE({}, '\', '\\'), '%%', '\%%'), '_', '\_')"
pattern_ops = {
- 'contains': r"LIKE '%%' || {} || '%%' ESCAPE '\'",
- 'icontains': r"LIKE '%%' || UPPER({}) || '%%' ESCAPE '\'",
- 'startswith': r"LIKE {} || '%%' ESCAPE '\'",
- 'istartswith': r"LIKE UPPER({}) || '%%' ESCAPE '\'",
- 'endswith': r"LIKE '%%' || {} ESCAPE '\'",
- 'iendswith': r"LIKE '%%' || UPPER({}) ESCAPE '\'",
+ "contains": r"LIKE '%%' || {} || '%%' ESCAPE '\'",
+ "icontains": r"LIKE '%%' || UPPER({}) || '%%' ESCAPE '\'",
+ "startswith": r"LIKE {} || '%%' ESCAPE '\'",
+ "istartswith": r"LIKE UPPER({}) || '%%' ESCAPE '\'",
+ "endswith": r"LIKE '%%' || {} ESCAPE '\'",
+ "iendswith": r"LIKE '%%' || UPPER({}) ESCAPE '\'",
}
Database = Database
@@ -141,14 +141,15 @@ class DatabaseWrapper(BaseDatabaseWrapper):
def get_connection_params(self):
settings_dict = self.settings_dict
- if not settings_dict['NAME']:
+ if not settings_dict["NAME"]:
raise ImproperlyConfigured(
"settings.DATABASES is improperly configured. "
- "Please supply the NAME value.")
+ "Please supply the NAME value."
+ )
kwargs = {
- 'database': settings_dict['NAME'],
- 'detect_types': Database.PARSE_DECLTYPES | Database.PARSE_COLNAMES,
- **settings_dict['OPTIONS'],
+ "database": settings_dict["NAME"],
+ "detect_types": Database.PARSE_DECLTYPES | Database.PARSE_COLNAMES,
+ **settings_dict["OPTIONS"],
}
# Always allow the underlying SQLite connection to be shareable
# between multiple threads. The safe-guarding will be handled at a
@@ -156,15 +157,15 @@ class DatabaseWrapper(BaseDatabaseWrapper):
# property. This is necessary as the shareability is disabled by
# default in pysqlite and it cannot be changed once a connection is
# opened.
- if 'check_same_thread' in kwargs and kwargs['check_same_thread']:
+ if "check_same_thread" in kwargs and kwargs["check_same_thread"]:
warnings.warn(
- 'The `check_same_thread` option was provided and set to '
- 'True. It will be overridden with False. Use the '
- '`DatabaseWrapper.allow_thread_sharing` property instead '
- 'for controlling thread shareability.',
- RuntimeWarning
+ "The `check_same_thread` option was provided and set to "
+ "True. It will be overridden with False. Use the "
+ "`DatabaseWrapper.allow_thread_sharing` property instead "
+ "for controlling thread shareability.",
+ RuntimeWarning,
)
- kwargs.update({'check_same_thread': False, 'uri': True})
+ kwargs.update({"check_same_thread": False, "uri": True})
return kwargs
@async_unsafe
@@ -172,10 +173,10 @@ class DatabaseWrapper(BaseDatabaseWrapper):
conn = Database.connect(**conn_params)
register_functions(conn)
- conn.execute('PRAGMA foreign_keys = ON')
+ conn.execute("PRAGMA foreign_keys = ON")
# The macOS bundled SQLite defaults legacy_alter_table ON, which
# prevents atomic table renames (feature supports_atomic_references_rename)
- conn.execute('PRAGMA legacy_alter_table = OFF')
+ conn.execute("PRAGMA legacy_alter_table = OFF")
return conn
def init_connection_state(self):
@@ -207,7 +208,7 @@ class DatabaseWrapper(BaseDatabaseWrapper):
else:
# sqlite3's internal default is ''. It's different from None.
# See Modules/_sqlite/connection.c.
- level = ''
+ level = ""
# 'isolation_level' is a misleading API.
# SQLite always runs at the SERIALIZABLE isolation level.
with self.wrap_database_errors:
@@ -215,16 +216,16 @@ class DatabaseWrapper(BaseDatabaseWrapper):
def disable_constraint_checking(self):
with self.cursor() as cursor:
- cursor.execute('PRAGMA foreign_keys = OFF')
+ cursor.execute("PRAGMA foreign_keys = OFF")
# Foreign key constraints cannot be turned off while in a multi-
# statement transaction. Fetch the current state of the pragma
# to determine if constraints are effectively disabled.
- enabled = cursor.execute('PRAGMA foreign_keys').fetchone()[0]
+ enabled = cursor.execute("PRAGMA foreign_keys").fetchone()[0]
return not bool(enabled)
def enable_constraint_checking(self):
with self.cursor() as cursor:
- cursor.execute('PRAGMA foreign_keys = ON')
+ cursor.execute("PRAGMA foreign_keys = ON")
def check_constraints(self, table_names=None):
"""
@@ -237,24 +238,32 @@ class DatabaseWrapper(BaseDatabaseWrapper):
if self.features.supports_pragma_foreign_key_check:
with self.cursor() as cursor:
if table_names is None:
- violations = cursor.execute('PRAGMA foreign_key_check').fetchall()
+ violations = cursor.execute("PRAGMA foreign_key_check").fetchall()
else:
violations = chain.from_iterable(
cursor.execute(
- 'PRAGMA foreign_key_check(%s)'
+ "PRAGMA foreign_key_check(%s)"
% self.ops.quote_name(table_name)
).fetchall()
for table_name in table_names
)
# See https://www.sqlite.org/pragma.html#pragma_foreign_key_check
- for table_name, rowid, referenced_table_name, foreign_key_index in violations:
+ for (
+ table_name,
+ rowid,
+ referenced_table_name,
+ foreign_key_index,
+ ) in violations:
foreign_key = cursor.execute(
- 'PRAGMA foreign_key_list(%s)' % self.ops.quote_name(table_name)
+ "PRAGMA foreign_key_list(%s)" % self.ops.quote_name(table_name)
).fetchall()[foreign_key_index]
column_name, referenced_column_name = foreign_key[3:5]
- primary_key_column_name = self.introspection.get_primary_key_column(cursor, table_name)
+ primary_key_column_name = self.introspection.get_primary_key_column(
+ cursor, table_name
+ )
primary_key_value, bad_value = cursor.execute(
- 'SELECT %s, %s FROM %s WHERE rowid = %%s' % (
+ "SELECT %s, %s FROM %s WHERE rowid = %%s"
+ % (
self.ops.quote_name(primary_key_column_name),
self.ops.quote_name(column_name),
self.ops.quote_name(table_name),
@@ -264,9 +273,15 @@ class DatabaseWrapper(BaseDatabaseWrapper):
raise 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
+ "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,
)
)
else:
@@ -274,11 +289,16 @@ class DatabaseWrapper(BaseDatabaseWrapper):
if table_names is None:
table_names = self.introspection.table_names(cursor)
for table_name in table_names:
- primary_key_column_name = self.introspection.get_primary_key_column(cursor, table_name)
+ primary_key_column_name = self.introspection.get_primary_key_column(
+ cursor, table_name
+ )
if not primary_key_column_name:
continue
relations = self.introspection.get_relations(cursor, table_name)
- for column_name, (referenced_column_name, referenced_table_name) in relations:
+ for column_name, (
+ referenced_column_name,
+ referenced_table_name,
+ ) in relations:
cursor.execute(
"""
SELECT REFERRING.`%s`, REFERRING.`%s` FROM `%s` as REFERRING
@@ -287,18 +307,29 @@ class DatabaseWrapper(BaseDatabaseWrapper):
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,
+ 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 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,
+ "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,
)
)
@@ -315,10 +346,10 @@ class DatabaseWrapper(BaseDatabaseWrapper):
self.cursor().execute("BEGIN")
def is_in_memory_db(self):
- return self.creation.is_in_memory_db(self.settings_dict['NAME'])
+ return self.creation.is_in_memory_db(self.settings_dict["NAME"])
-FORMAT_QMARK_REGEX = _lazy_re_compile(r'(?<!%)%s')
+FORMAT_QMARK_REGEX = _lazy_re_compile(r"(?<!%)%s")
class SQLiteCursorWrapper(Database.Cursor):
@@ -327,6 +358,7 @@ class SQLiteCursorWrapper(Database.Cursor):
This fixes it -- but note that if you want to use a literal "%s" in a query,
you'll need to use "%%s".
"""
+
def execute(self, query, params=None):
if params is None:
return Database.Cursor.execute(self, query)
@@ -338,4 +370,4 @@ class SQLiteCursorWrapper(Database.Cursor):
return Database.Cursor.executemany(self, query, param_list)
def convert_query(self, query):
- return FORMAT_QMARK_REGEX.sub('?', query).replace('%%', '%')
+ return FORMAT_QMARK_REGEX.sub("?", query).replace("%%", "%")