import operator import platform from django.db import transaction from django.db.backends.base.features import BaseDatabaseFeatures from django.db.utils import OperationalError from django.utils.functional import cached_property from .base import Database class DatabaseFeatures(BaseDatabaseFeatures): # SQLite can read from a cursor since SQLite 3.6.5, subject to the caveat # that statements within a connection aren't isolated from each other. See # https://sqlite.org/isolation.html. can_use_chunked_reads = True test_db_allows_multiple_connections = False supports_unspecified_pk = True supports_timezones = False max_query_params = 999 supports_mixed_date_datetime_comparisons = False supports_transactions = True atomic_transactions = False can_rollback_ddl = True can_create_inline_fk = False supports_paramstyle_pyformat = False can_clone_databases = True supports_temporal_subtraction = True ignores_table_name_case = True supports_cast_with_precision = False time_cast_precision = 3 can_release_savepoints = True # Is "ALTER TABLE ... RENAME COLUMN" supported? can_alter_table_rename_column = Database.sqlite_version_info >= (3, 25, 0) supports_parentheses_in_compound = False # Deferred constraint checks can be emulated on SQLite < 3.20 but not in a # reasonably performant way. supports_pragma_foreign_key_check = Database.sqlite_version_info >= (3, 20, 0) can_defer_constraint_checks = supports_pragma_foreign_key_check supports_functions_in_partial_indexes = Database.sqlite_version_info >= (3, 15, 0) supports_over_clause = Database.sqlite_version_info >= (3, 25, 0) supports_frame_range_fixed_distance = Database.sqlite_version_info >= (3, 28, 0) supports_aggregate_filter_clause = Database.sqlite_version_info >= (3, 30, 1) supports_order_by_nulls_modifier = Database.sqlite_version_info >= (3, 30, 0) order_by_nulls_first = True supports_json_field_contains = False test_collations = { 'ci': 'nocase', 'cs': 'binary', 'non_default': 'nocase', } @cached_property def django_test_skips(self): skips = { 'SQLite stores values rounded to 15 significant digits.': { 'model_fields.test_decimalfield.DecimalFieldTests.test_fetch_from_db_without_float_rounding', }, 'SQLite naively remakes the table on field alteration.': { 'schema.tests.SchemaTests.test_unique_no_unnecessary_fk_drops', 'schema.tests.SchemaTests.test_unique_and_reverse_m2m', 'schema.tests.SchemaTests.test_alter_field_default_doesnt_perform_queries', 'schema.tests.SchemaTests.test_rename_column_renames_deferred_sql_references', }, "SQLite doesn't have a constraint.": { 'model_fields.test_integerfield.PositiveIntegerFieldTests.test_negative_values', }, } if Database.sqlite_version_info < (3, 27): skips.update({ 'Nondeterministic failure on SQLite < 3.27.': { 'expressions_window.tests.WindowFunctionTests.test_subquery_row_range_rank', }, }) if self.connection.is_in_memory_db(): skips.update({ "the sqlite backend's close() method is a no-op when using an " "in-memory database": { 'servers.test_liveserverthread.LiveServerThreadTest.test_closes_connections', }, }) return skips @cached_property def supports_atomic_references_rename(self): # SQLite 3.28.0 bundled with MacOS 10.15 does not support renaming # references atomically. if platform.mac_ver()[0].startswith('10.15.') and Database.sqlite_version_info == (3, 28, 0): return False return Database.sqlite_version_info >= (3, 26, 0) @cached_property def introspected_field_types(self): return{ **super().introspected_field_types, 'BigAutoField': 'AutoField', 'DurationField': 'BigIntegerField', 'GenericIPAddressField': 'CharField', 'SmallAutoField': 'AutoField', } @cached_property def supports_json_field(self): with self.connection.cursor() as cursor: try: with transaction.atomic(self.connection.alias): cursor.execute('SELECT JSON(\'{"a": "b"}\')') except OperationalError: return False return True can_introspect_json_field = property(operator.attrgetter('supports_json_field')) has_json_object_function = property(operator.attrgetter('supports_json_field'))