features.py 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. import operator
  2. from django.db import DataError, InterfaceError
  3. from django.db.backends.base.features import BaseDatabaseFeatures
  4. from django.db.backends.postgresql.psycopg_any import is_psycopg3
  5. from django.utils.functional import cached_property
  6. class DatabaseFeatures(BaseDatabaseFeatures):
  7. minimum_database_version = (13,)
  8. allows_group_by_selected_pks = True
  9. can_return_columns_from_insert = True
  10. can_return_rows_from_bulk_insert = True
  11. has_real_datatype = True
  12. has_native_uuid_field = True
  13. has_native_duration_field = True
  14. has_native_json_field = True
  15. can_defer_constraint_checks = True
  16. has_select_for_update = True
  17. has_select_for_update_nowait = True
  18. has_select_for_update_of = True
  19. has_select_for_update_skip_locked = True
  20. has_select_for_no_key_update = True
  21. can_release_savepoints = True
  22. supports_comments = True
  23. supports_tablespaces = True
  24. supports_transactions = True
  25. can_introspect_materialized_views = True
  26. can_distinct_on_fields = True
  27. can_rollback_ddl = True
  28. schema_editor_uses_clientside_param_binding = True
  29. supports_combined_alters = True
  30. nulls_order_largest = True
  31. closed_cursor_error_class = InterfaceError
  32. greatest_least_ignores_nulls = True
  33. can_clone_databases = True
  34. supports_temporal_subtraction = True
  35. supports_slicing_ordering_in_compound = True
  36. create_test_procedure_without_params_sql = """
  37. CREATE FUNCTION test_procedure () RETURNS void AS $$
  38. DECLARE
  39. V_I INTEGER;
  40. BEGIN
  41. V_I := 1;
  42. END;
  43. $$ LANGUAGE plpgsql;"""
  44. create_test_procedure_with_int_param_sql = """
  45. CREATE FUNCTION test_procedure (P_I INTEGER) RETURNS void AS $$
  46. DECLARE
  47. V_I INTEGER;
  48. BEGIN
  49. V_I := P_I;
  50. END;
  51. $$ LANGUAGE plpgsql;"""
  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. requires_casted_case_in_updates = True
  60. supports_over_clause = True
  61. supports_frame_exclusion = True
  62. only_supports_unbounded_with_preceding_and_following = True
  63. supports_aggregate_filter_clause = True
  64. supported_explain_formats = {"JSON", "TEXT", "XML", "YAML"}
  65. supports_deferrable_unique_constraints = True
  66. has_json_operators = True
  67. json_key_contains_list_matching_requires_list = True
  68. supports_update_conflicts = True
  69. supports_update_conflicts_with_target = True
  70. supports_covering_indexes = True
  71. supports_stored_generated_columns = True
  72. supports_virtual_generated_columns = False
  73. can_rename_index = True
  74. test_collations = {
  75. "deterministic": "C",
  76. "non_default": "sv-x-icu",
  77. "swedish_ci": "sv-x-icu",
  78. "virtual": "sv-x-icu",
  79. }
  80. test_now_utc_template = "STATEMENT_TIMESTAMP() AT TIME ZONE 'UTC'"
  81. insert_test_table_with_defaults = "INSERT INTO {} DEFAULT VALUES"
  82. @cached_property
  83. def django_test_skips(self):
  84. skips = {
  85. "opclasses are PostgreSQL only.": {
  86. "indexes.tests.SchemaIndexesNotPostgreSQLTests."
  87. "test_create_index_ignores_opclasses",
  88. },
  89. "PostgreSQL requires casting to text.": {
  90. "lookup.tests.LookupTests.test_textfield_exact_null",
  91. },
  92. }
  93. if self.connection.settings_dict["OPTIONS"].get("pool"):
  94. skips.update(
  95. {
  96. "Pool does implicit health checks": {
  97. "backends.base.test_base.ConnectionHealthChecksTests."
  98. "test_health_checks_enabled",
  99. "backends.base.test_base.ConnectionHealthChecksTests."
  100. "test_set_autocommit_health_checks_enabled",
  101. },
  102. }
  103. )
  104. if self.uses_server_side_binding:
  105. skips.update(
  106. {
  107. "The actual query cannot be determined for server side bindings": {
  108. "backends.base.test_base.ExecuteWrapperTests."
  109. "test_wrapper_debug",
  110. }
  111. },
  112. )
  113. return skips
  114. @cached_property
  115. def django_test_expected_failures(self):
  116. expected_failures = set()
  117. if self.uses_server_side_binding:
  118. expected_failures.update(
  119. {
  120. # Parameters passed to expressions in SELECT and GROUP BY
  121. # clauses are not recognized as the same values when using
  122. # server-side binding cursors (#34255).
  123. "aggregation.tests.AggregateTestCase."
  124. "test_group_by_nested_expression_with_params",
  125. }
  126. )
  127. return expected_failures
  128. @cached_property
  129. def uses_server_side_binding(self):
  130. options = self.connection.settings_dict["OPTIONS"]
  131. return is_psycopg3 and options.get("server_side_binding") is True
  132. @cached_property
  133. def prohibits_null_characters_in_text_exception(self):
  134. if is_psycopg3:
  135. return DataError, "PostgreSQL text fields cannot contain NUL (0x00) bytes"
  136. else:
  137. return ValueError, "A string literal cannot contain NUL (0x00) characters."
  138. @cached_property
  139. def introspected_field_types(self):
  140. return {
  141. **super().introspected_field_types,
  142. "PositiveBigIntegerField": "BigIntegerField",
  143. "PositiveIntegerField": "IntegerField",
  144. "PositiveSmallIntegerField": "SmallIntegerField",
  145. }
  146. @cached_property
  147. def is_postgresql_14(self):
  148. return self.connection.pg_version >= 140000
  149. @cached_property
  150. def is_postgresql_15(self):
  151. return self.connection.pg_version >= 150000
  152. @cached_property
  153. def is_postgresql_16(self):
  154. return self.connection.pg_version >= 160000
  155. has_bit_xor = property(operator.attrgetter("is_postgresql_14"))
  156. supports_covering_spgist_indexes = property(operator.attrgetter("is_postgresql_14"))
  157. supports_unlimited_charfield = True
  158. supports_nulls_distinct_unique_constraints = property(
  159. operator.attrgetter("is_postgresql_15")
  160. )