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 )