Plain is headed towards 1.0! Subscribe for development updates →

 1import operator
 2
 3from plain.models import transaction
 4from plain.models.backends.base.features import BaseDatabaseFeatures
 5from plain.models.db import OperationalError
 6from plain.utils.functional import cached_property
 7
 8from .base import Database
 9
10
11class DatabaseFeatures(BaseDatabaseFeatures):
12    minimum_database_version = (3, 21)
13    test_db_allows_multiple_connections = False
14    supports_unspecified_pk = True
15    supports_timezones = False
16    max_query_params = 999
17    supports_transactions = True
18    atomic_transactions = False
19    can_rollback_ddl = True
20    can_create_inline_fk = False
21    requires_literal_defaults = True
22    can_clone_databases = True
23    supports_temporal_subtraction = True
24    ignores_table_name_case = True
25    supports_cast_with_precision = False
26    time_cast_precision = 3
27    can_release_savepoints = True
28    has_case_insensitive_like = True
29    # Is "ALTER TABLE ... RENAME COLUMN" supported?
30    can_alter_table_rename_column = Database.sqlite_version_info >= (3, 25, 0)
31    # Is "ALTER TABLE ... DROP COLUMN" supported?
32    can_alter_table_drop_column = Database.sqlite_version_info >= (3, 35, 5)
33    supports_parentheses_in_compound = False
34    can_defer_constraint_checks = True
35    supports_over_clause = Database.sqlite_version_info >= (3, 25, 0)
36    supports_frame_range_fixed_distance = Database.sqlite_version_info >= (3, 28, 0)
37    supports_aggregate_filter_clause = Database.sqlite_version_info >= (3, 30, 1)
38    supports_order_by_nulls_modifier = Database.sqlite_version_info >= (3, 30, 0)
39    # NULLS LAST/FIRST emulation on < 3.30 requires subquery wrapping.
40    requires_compound_order_by_subquery = Database.sqlite_version_info < (3, 30)
41    order_by_nulls_first = True
42    supports_json_field_contains = False
43    supports_update_conflicts = Database.sqlite_version_info >= (3, 24, 0)
44    supports_update_conflicts_with_target = supports_update_conflicts
45    test_collations = {
46        "ci": "nocase",
47        "cs": "binary",
48        "non_default": "nocase",
49    }
50    create_test_table_with_composite_primary_key = """
51        CREATE TABLE test_table_composite_pk (
52            column_1 INTEGER NOT NULL,
53            column_2 INTEGER NOT NULL,
54            PRIMARY KEY(column_1, column_2)
55        )
56    """
57
58    @cached_property
59    def supports_atomic_references_rename(self):
60        return Database.sqlite_version_info >= (3, 26, 0)
61
62    @cached_property
63    def introspected_field_types(self):
64        return {
65            **super().introspected_field_types,
66            "BigAutoField": "AutoField",
67            "DurationField": "BigIntegerField",
68            "GenericIPAddressField": "CharField",
69            "SmallAutoField": "AutoField",
70        }
71
72    @cached_property
73    def supports_json_field(self):
74        with self.connection.cursor() as cursor:
75            try:
76                with transaction.atomic(self.connection.alias):
77                    cursor.execute('SELECT JSON(\'{"a": "b"}\')')
78            except OperationalError:
79                return False
80        return True
81
82    can_introspect_json_field = property(operator.attrgetter("supports_json_field"))
83    has_json_object_function = property(operator.attrgetter("supports_json_field"))
84
85    @cached_property
86    def can_return_columns_from_insert(self):
87        return Database.sqlite_version_info >= (3, 35)
88
89    can_return_rows_from_bulk_insert = property(
90        operator.attrgetter("can_return_columns_from_insert")
91    )