166 lines
6.7 KiB
Python
166 lines
6.7 KiB
Python
|
import operator
|
||
|
|
||
|
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):
|
||
|
minimum_database_version = (3, 21)
|
||
|
test_db_allows_multiple_connections = False
|
||
|
supports_unspecified_pk = True
|
||
|
supports_timezones = False
|
||
|
max_query_params = 999
|
||
|
supports_transactions = True
|
||
|
atomic_transactions = False
|
||
|
can_rollback_ddl = True
|
||
|
can_create_inline_fk = False
|
||
|
requires_literal_defaults = True
|
||
|
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
|
||
|
has_case_insensitive_like = True
|
||
|
# Is "ALTER TABLE ... RENAME COLUMN" supported?
|
||
|
can_alter_table_rename_column = Database.sqlite_version_info >= (3, 25, 0)
|
||
|
# Is "ALTER TABLE ... DROP COLUMN" supported?
|
||
|
can_alter_table_drop_column = Database.sqlite_version_info >= (3, 35, 5)
|
||
|
supports_parentheses_in_compound = False
|
||
|
can_defer_constraint_checks = True
|
||
|
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)
|
||
|
# NULLS LAST/FIRST emulation on < 3.30 requires subquery wrapping.
|
||
|
requires_compound_order_by_subquery = Database.sqlite_version_info < (3, 30)
|
||
|
order_by_nulls_first = True
|
||
|
supports_json_field_contains = False
|
||
|
supports_update_conflicts = Database.sqlite_version_info >= (3, 24, 0)
|
||
|
supports_update_conflicts_with_target = supports_update_conflicts
|
||
|
test_collations = {
|
||
|
"ci": "nocase",
|
||
|
"cs": "binary",
|
||
|
"non_default": "nocase",
|
||
|
}
|
||
|
django_test_expected_failures = {
|
||
|
# The django_format_dtdelta() function doesn't properly handle mixed
|
||
|
# Date/DateTime fields and timedeltas.
|
||
|
"expressions.tests.FTimeDeltaTests.test_mixed_comparisons1",
|
||
|
}
|
||
|
create_test_table_with_composite_primary_key = """
|
||
|
CREATE TABLE test_table_composite_pk (
|
||
|
column_1 INTEGER NOT NULL,
|
||
|
column_2 INTEGER NOT NULL,
|
||
|
PRIMARY KEY(column_1, column_2)
|
||
|
)
|
||
|
"""
|
||
|
|
||
|
@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 support negative precision for ROUND().": {
|
||
|
"db_functions.math.test_round.RoundTests."
|
||
|
"test_null_with_negative_precision",
|
||
|
"db_functions.math.test_round.RoundTests."
|
||
|
"test_decimal_with_negative_precision",
|
||
|
"db_functions.math.test_round.RoundTests."
|
||
|
"test_float_with_negative_precision",
|
||
|
"db_functions.math.test_round.RoundTests."
|
||
|
"test_integer_with_negative_precision",
|
||
|
},
|
||
|
}
|
||
|
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",
|
||
|
"servers.tests.LiveServerTestCloseConnectionTest."
|
||
|
"test_closes_connections",
|
||
|
},
|
||
|
"For SQLite in-memory tests, closing the connection destroys"
|
||
|
"the database.": {
|
||
|
"test_utils.tests.AssertNumQueriesUponConnectionTests."
|
||
|
"test_ignores_connection_configuration_queries",
|
||
|
},
|
||
|
}
|
||
|
)
|
||
|
else:
|
||
|
skips.update(
|
||
|
{
|
||
|
"Only connections to in-memory SQLite databases are passed to the "
|
||
|
"server thread.": {
|
||
|
"servers.tests.LiveServerInMemoryDatabaseLockTest."
|
||
|
"test_in_memory_database_lock",
|
||
|
},
|
||
|
"multiprocessing's start method is checked only for in-memory "
|
||
|
"SQLite databases": {
|
||
|
"backends.sqlite.test_creation.TestDbSignatureTests."
|
||
|
"test_get_test_db_clone_settings_not_supported",
|
||
|
},
|
||
|
}
|
||
|
)
|
||
|
return skips
|
||
|
|
||
|
@cached_property
|
||
|
def supports_atomic_references_rename(self):
|
||
|
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"))
|
||
|
|
||
|
@cached_property
|
||
|
def can_return_columns_from_insert(self):
|
||
|
return Database.sqlite_version_info >= (3, 35)
|
||
|
|
||
|
can_return_rows_from_bulk_insert = property(
|
||
|
operator.attrgetter("can_return_columns_from_insert")
|
||
|
)
|