1from __future__ import annotations
2
3import operator
4from functools import cached_property
5
6from plain.models import transaction
7from plain.models.backends.base.features import BaseDatabaseFeatures
8from plain.models.db import OperationalError
9
10from .base import Database
11
12
13class DatabaseFeatures(BaseDatabaseFeatures):
14 minimum_database_version = (3, 21)
15 max_query_params = 999
16 supports_transactions = True
17 can_rollback_ddl = True
18 requires_literal_defaults = True
19 supports_temporal_subtraction = True
20 ignores_table_name_case = True
21 # Is "ALTER TABLE ... RENAME COLUMN" supported?
22 can_alter_table_rename_column = Database.sqlite_version_info >= (3, 25, 0)
23 # Is "ALTER TABLE ... DROP COLUMN" supported?
24 can_alter_table_drop_column = Database.sqlite_version_info >= (3, 35, 5)
25 supports_parentheses_in_compound = False
26 can_defer_constraint_checks = True
27 supports_over_clause = Database.sqlite_version_info >= (3, 25, 0)
28 supports_aggregate_filter_clause = Database.sqlite_version_info >= (3, 30, 1)
29 supports_order_by_nulls_modifier = Database.sqlite_version_info >= (3, 30, 0)
30 # NULLS LAST/FIRST emulation on < 3.30 requires subquery wrapping.
31 requires_compound_order_by_subquery = Database.sqlite_version_info < (3, 30)
32 order_by_nulls_first = True
33 supports_json_field_contains = False
34 supports_update_conflicts = Database.sqlite_version_info >= (3, 24, 0)
35 supports_update_conflicts_with_target = supports_update_conflicts
36 supports_unlimited_charfield = True
37
38 @cached_property
39 def supports_atomic_references_rename(self) -> bool:
40 return Database.sqlite_version_info >= (3, 26, 0)
41
42 @cached_property
43 def supports_json_field(self) -> bool:
44 with self.connection.cursor() as cursor:
45 try:
46 with transaction.atomic():
47 cursor.execute('SELECT JSON(\'{"a": "b"}\')')
48 except OperationalError:
49 return False
50 return True
51
52 can_introspect_json_field = property(operator.attrgetter("supports_json_field"))
53 has_json_object_function = property(operator.attrgetter("supports_json_field"))
54
55 @cached_property
56 def can_return_columns_from_insert(self) -> bool:
57 return Database.sqlite_version_info >= (3, 35)
58
59 can_return_rows_from_bulk_insert = property(
60 operator.attrgetter("can_return_columns_from_insert")
61 )