features.py 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. import operator
  2. from django.db import transaction
  3. from django.db.backends.base.features import BaseDatabaseFeatures
  4. from django.db.utils import OperationalError
  5. from django.utils.functional import cached_property
  6. from .base import Database
  7. class DatabaseFeatures(BaseDatabaseFeatures):
  8. minimum_database_version = (3, 27)
  9. test_db_allows_multiple_connections = False
  10. supports_unspecified_pk = True
  11. supports_timezones = False
  12. max_query_params = 999
  13. supports_transactions = True
  14. atomic_transactions = False
  15. can_rollback_ddl = True
  16. can_create_inline_fk = False
  17. requires_literal_defaults = True
  18. can_clone_databases = True
  19. supports_temporal_subtraction = True
  20. ignores_table_name_case = True
  21. supports_cast_with_precision = False
  22. time_cast_precision = 3
  23. can_release_savepoints = True
  24. has_case_insensitive_like = True
  25. # Is "ALTER TABLE ... DROP COLUMN" supported?
  26. can_alter_table_drop_column = Database.sqlite_version_info >= (3, 35, 5)
  27. supports_parentheses_in_compound = False
  28. can_defer_constraint_checks = True
  29. supports_over_clause = True
  30. supports_frame_range_fixed_distance = Database.sqlite_version_info >= (3, 28, 0)
  31. supports_aggregate_filter_clause = Database.sqlite_version_info >= (3, 30, 1)
  32. supports_order_by_nulls_modifier = Database.sqlite_version_info >= (3, 30, 0)
  33. # NULLS LAST/FIRST emulation on < 3.30 requires subquery wrapping.
  34. requires_compound_order_by_subquery = Database.sqlite_version_info < (3, 30)
  35. order_by_nulls_first = True
  36. supports_json_field_contains = False
  37. supports_update_conflicts = True
  38. supports_update_conflicts_with_target = True
  39. supports_stored_generated_columns = Database.sqlite_version_info >= (3, 31, 0)
  40. supports_virtual_generated_columns = Database.sqlite_version_info >= (3, 31, 0)
  41. test_collations = {
  42. "ci": "nocase",
  43. "cs": "binary",
  44. "non_default": "nocase",
  45. "virtual": "nocase",
  46. }
  47. django_test_expected_failures = {
  48. # The django_format_dtdelta() function doesn't properly handle mixed
  49. # Date/DateTime fields and timedeltas.
  50. "expressions.tests.FTimeDeltaTests.test_mixed_comparisons1",
  51. }
  52. create_test_table_with_composite_primary_key = """
  53. CREATE TABLE test_table_composite_pk (
  54. column_1 INTEGER NOT NULL,
  55. column_2 INTEGER NOT NULL,
  56. PRIMARY KEY(column_1, column_2)
  57. )
  58. """
  59. insert_test_table_with_defaults = 'INSERT INTO {} ("null") VALUES (1)'
  60. supports_default_keyword_in_insert = False
  61. @cached_property
  62. def django_test_skips(self):
  63. skips = {
  64. "SQLite stores values rounded to 15 significant digits.": {
  65. "model_fields.test_decimalfield.DecimalFieldTests."
  66. "test_fetch_from_db_without_float_rounding",
  67. },
  68. "SQLite naively remakes the table on field alteration.": {
  69. "schema.tests.SchemaTests.test_unique_no_unnecessary_fk_drops",
  70. "schema.tests.SchemaTests.test_unique_and_reverse_m2m",
  71. "schema.tests.SchemaTests."
  72. "test_alter_field_default_doesnt_perform_queries",
  73. "schema.tests.SchemaTests."
  74. "test_rename_column_renames_deferred_sql_references",
  75. },
  76. "SQLite doesn't support negative precision for ROUND().": {
  77. "db_functions.math.test_round.RoundTests."
  78. "test_null_with_negative_precision",
  79. "db_functions.math.test_round.RoundTests."
  80. "test_decimal_with_negative_precision",
  81. "db_functions.math.test_round.RoundTests."
  82. "test_float_with_negative_precision",
  83. "db_functions.math.test_round.RoundTests."
  84. "test_integer_with_negative_precision",
  85. },
  86. }
  87. if self.connection.is_in_memory_db():
  88. skips.update(
  89. {
  90. "the sqlite backend's close() method is a no-op when using an "
  91. "in-memory database": {
  92. "servers.test_liveserverthread.LiveServerThreadTest."
  93. "test_closes_connections",
  94. "servers.tests.LiveServerTestCloseConnectionTest."
  95. "test_closes_connections",
  96. },
  97. "For SQLite in-memory tests, closing the connection destroys"
  98. "the database.": {
  99. "test_utils.tests.AssertNumQueriesUponConnectionTests."
  100. "test_ignores_connection_configuration_queries",
  101. },
  102. }
  103. )
  104. else:
  105. skips.update(
  106. {
  107. "Only connections to in-memory SQLite databases are passed to the "
  108. "server thread.": {
  109. "servers.tests.LiveServerInMemoryDatabaseLockTest."
  110. "test_in_memory_database_lock",
  111. },
  112. "multiprocessing's start method is checked only for in-memory "
  113. "SQLite databases": {
  114. "backends.sqlite.test_creation.TestDbSignatureTests."
  115. "test_get_test_db_clone_settings_not_supported",
  116. },
  117. }
  118. )
  119. return skips
  120. @cached_property
  121. def introspected_field_types(self):
  122. return {
  123. **super().introspected_field_types,
  124. "BigAutoField": "AutoField",
  125. "DurationField": "BigIntegerField",
  126. "GenericIPAddressField": "CharField",
  127. "SmallAutoField": "AutoField",
  128. }
  129. @cached_property
  130. def supports_json_field(self):
  131. with self.connection.cursor() as cursor:
  132. try:
  133. with transaction.atomic(self.connection.alias):
  134. cursor.execute('SELECT JSON(\'{"a": "b"}\')')
  135. except OperationalError:
  136. return False
  137. return True
  138. can_introspect_json_field = property(operator.attrgetter("supports_json_field"))
  139. has_json_object_function = property(operator.attrgetter("supports_json_field"))
  140. @cached_property
  141. def can_return_columns_from_insert(self):
  142. return Database.sqlite_version_info >= (3, 35)
  143. can_return_rows_from_bulk_insert = property(
  144. operator.attrgetter("can_return_columns_from_insert")
  145. )