Plain is headed towards 1.0! Subscribe for development updates →

  1import operator
  2
  3from plain.models.backends.base.features import BaseDatabaseFeatures
  4from plain.models.backends.postgresql.psycopg_any import is_psycopg3
  5from plain.models.db import DataError, InterfaceError
  6from plain.utils.functional import cached_property
  7
  8
  9class DatabaseFeatures(BaseDatabaseFeatures):
 10    minimum_database_version = (12,)
 11    allows_group_by_selected_pks = True
 12    can_return_columns_from_insert = True
 13    can_return_rows_from_bulk_insert = True
 14    has_real_datatype = True
 15    has_native_uuid_field = True
 16    has_native_duration_field = True
 17    has_native_json_field = True
 18    can_defer_constraint_checks = True
 19    has_select_for_update = True
 20    has_select_for_update_nowait = True
 21    has_select_for_update_of = True
 22    has_select_for_update_skip_locked = True
 23    has_select_for_no_key_update = True
 24    can_release_savepoints = True
 25    supports_comments = True
 26    supports_tablespaces = True
 27    supports_transactions = True
 28    can_introspect_materialized_views = True
 29    can_distinct_on_fields = True
 30    can_rollback_ddl = True
 31    schema_editor_uses_clientside_param_binding = True
 32    supports_combined_alters = True
 33    nulls_order_largest = True
 34    closed_cursor_error_class = InterfaceError
 35    greatest_least_ignores_nulls = True
 36    can_clone_databases = True
 37    supports_temporal_subtraction = True
 38    supports_slicing_ordering_in_compound = True
 39    create_test_procedure_without_params_sql = """
 40        CREATE FUNCTION test_procedure () RETURNS void AS $$
 41        DECLARE
 42            V_I INTEGER;
 43        BEGIN
 44            V_I := 1;
 45        END;
 46    $$ LANGUAGE plpgsql;"""
 47    create_test_procedure_with_int_param_sql = """
 48        CREATE FUNCTION test_procedure (P_I INTEGER) RETURNS void AS $$
 49        DECLARE
 50            V_I INTEGER;
 51        BEGIN
 52            V_I := P_I;
 53        END;
 54    $$ LANGUAGE plpgsql;"""
 55    create_test_table_with_composite_primary_key = """
 56        CREATE TABLE test_table_composite_pk (
 57            column_1 INTEGER NOT NULL,
 58            column_2 INTEGER NOT NULL,
 59            PRIMARY KEY(column_1, column_2)
 60        )
 61    """
 62    requires_casted_case_in_updates = True
 63    supports_over_clause = True
 64    only_supports_unbounded_with_preceding_and_following = True
 65    supports_aggregate_filter_clause = True
 66    supported_explain_formats = {"JSON", "TEXT", "XML", "YAML"}
 67    supports_deferrable_unique_constraints = True
 68    has_json_operators = True
 69    json_key_contains_list_matching_requires_list = True
 70    supports_update_conflicts = True
 71    supports_update_conflicts_with_target = True
 72    supports_covering_indexes = True
 73    can_rename_index = True
 74    test_collations = {
 75        "non_default": "sv-x-icu",
 76        "swedish_ci": "sv-x-icu",
 77    }
 78    test_now_utc_template = "STATEMENT_TIMESTAMP() AT TIME ZONE 'UTC'"
 79
 80    @cached_property
 81    def uses_server_side_binding(self):
 82        options = self.connection.settings_dict["OPTIONS"]
 83        return is_psycopg3 and options.get("server_side_binding") is True
 84
 85    @cached_property
 86    def prohibits_null_characters_in_text_exception(self):
 87        if is_psycopg3:
 88            return DataError, "PostgreSQL text fields cannot contain NUL (0x00) bytes"
 89        else:
 90            return ValueError, "A string literal cannot contain NUL (0x00) characters."
 91
 92    @cached_property
 93    def introspected_field_types(self):
 94        return {
 95            **super().introspected_field_types,
 96            "PositiveBigIntegerField": "BigIntegerField",
 97            "PositiveIntegerField": "IntegerField",
 98            "PositiveSmallIntegerField": "SmallIntegerField",
 99        }
100
101    @cached_property
102    def is_postgresql_13(self):
103        return self.connection.pg_version >= 130000
104
105    @cached_property
106    def is_postgresql_14(self):
107        return self.connection.pg_version >= 140000
108
109    has_bit_xor = property(operator.attrgetter("is_postgresql_14"))
110    supports_covering_spgist_indexes = property(operator.attrgetter("is_postgresql_14"))
111    supports_unlimited_charfield = True