features.py 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  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 = (12,)
  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. only_supports_unbounded_with_preceding_and_following = True
  62. supports_aggregate_filter_clause = True
  63. supported_explain_formats = {"JSON", "TEXT", "XML", "YAML"}
  64. supports_deferrable_unique_constraints = True
  65. has_json_operators = True
  66. json_key_contains_list_matching_requires_list = True
  67. supports_update_conflicts = True
  68. supports_update_conflicts_with_target = True
  69. supports_covering_indexes = True
  70. supports_stored_generated_columns = True
  71. supports_virtual_generated_columns = False
  72. can_rename_index = True
  73. test_collations = {
  74. "deterministic": "C",
  75. "non_default": "sv-x-icu",
  76. "swedish_ci": "sv-x-icu",
  77. "virtual": "sv-x-icu",
  78. }
  79. test_now_utc_template = "STATEMENT_TIMESTAMP() AT TIME ZONE 'UTC'"
  80. insert_test_table_with_defaults = "INSERT INTO {} DEFAULT VALUES"
  81. django_test_skips = {
  82. "opclasses are PostgreSQL only.": {
  83. "indexes.tests.SchemaIndexesNotPostgreSQLTests."
  84. "test_create_index_ignores_opclasses",
  85. },
  86. "PostgreSQL requires casting to text.": {
  87. "lookup.tests.LookupTests.test_textfield_exact_null",
  88. },
  89. }
  90. @cached_property
  91. def django_test_expected_failures(self):
  92. expected_failures = set()
  93. if self.uses_server_side_binding:
  94. expected_failures.update(
  95. {
  96. # Parameters passed to expressions in SELECT and GROUP BY
  97. # clauses are not recognized as the same values when using
  98. # server-side binding cursors (#34255).
  99. "aggregation.tests.AggregateTestCase."
  100. "test_group_by_nested_expression_with_params",
  101. }
  102. )
  103. return expected_failures
  104. @cached_property
  105. def uses_server_side_binding(self):
  106. options = self.connection.settings_dict["OPTIONS"]
  107. return is_psycopg3 and options.get("server_side_binding") is True
  108. @cached_property
  109. def prohibits_null_characters_in_text_exception(self):
  110. if is_psycopg3:
  111. return DataError, "PostgreSQL text fields cannot contain NUL (0x00) bytes"
  112. else:
  113. return ValueError, "A string literal cannot contain NUL (0x00) characters."
  114. @cached_property
  115. def introspected_field_types(self):
  116. return {
  117. **super().introspected_field_types,
  118. "PositiveBigIntegerField": "BigIntegerField",
  119. "PositiveIntegerField": "IntegerField",
  120. "PositiveSmallIntegerField": "SmallIntegerField",
  121. }
  122. @cached_property
  123. def is_postgresql_13(self):
  124. return self.connection.pg_version >= 130000
  125. @cached_property
  126. def is_postgresql_14(self):
  127. return self.connection.pg_version >= 140000
  128. @cached_property
  129. def is_postgresql_15(self):
  130. return self.connection.pg_version >= 150000
  131. has_bit_xor = property(operator.attrgetter("is_postgresql_14"))
  132. supports_covering_spgist_indexes = property(operator.attrgetter("is_postgresql_14"))
  133. supports_unlimited_charfield = True
  134. supports_nulls_distinct_unique_constraints = property(
  135. operator.attrgetter("is_postgresql_15")
  136. )