1from __future__ import annotations
2
3from functools import cached_property
4from typing import TYPE_CHECKING
5
6if TYPE_CHECKING:
7 from plain.models.backends.base.base import BaseDatabaseWrapper
8
9
10class BaseDatabaseFeatures:
11 # An optional tuple indicating the minimum supported database version.
12 minimum_database_version: tuple[int, ...] | None = None
13 allows_group_by_selected_pks = False
14 allows_group_by_select_index = True
15 empty_fetchmany_value = []
16 update_can_self_select = True
17
18 # Does the backend support initially deferrable unique constraints?
19 supports_deferrable_unique_constraints = False
20
21 can_use_chunked_reads = True
22 can_return_columns_from_insert = False
23 can_return_rows_from_bulk_insert = False
24 has_bulk_insert = True
25 uses_savepoints = True
26
27 # If True, don't use integer foreign keys referring to, e.g., positive
28 # integer primary keys.
29 related_fields_match_type = False
30 has_select_for_update = False
31 has_select_for_update_nowait = False
32 has_select_for_update_skip_locked = False
33 has_select_for_update_of = False
34 has_select_for_no_key_update = False
35
36 # Does the backend truncate names properly when they are too long?
37 truncates_names = False
38
39 # Does the backend ignore unnecessary ORDER BY clauses in subqueries?
40 ignores_unnecessary_order_by_in_subqueries = True
41
42 # Is there a true datatype for uuid?
43 has_native_uuid_field = False
44
45 # Is there a true datatype for timedeltas?
46 has_native_duration_field = False
47
48 # Does the database driver supports same type temporal data subtraction
49 # by returning the type used to store duration field?
50 supports_temporal_subtraction = False
51
52 # Does the database have a copy of the zoneinfo database?
53 has_zoneinfo_database = True
54
55 # Does the backend support NULLS FIRST and NULLS LAST in ORDER BY?
56 supports_order_by_nulls_modifier = True
57
58 # Does the backend orders NULLS FIRST by default?
59 order_by_nulls_first = False
60
61 # The database's limit on the number of query parameters.
62 max_query_params = None
63
64 # Can an object have an autoincrement primary key of 0?
65 allows_auto_pk_0 = True
66
67 # Do we need to NULL a ForeignKeyField out, or can the constraint check be
68 # deferred
69 can_defer_constraint_checks = False
70
71 # Can the backend introspect the column order (ASC/DESC) for indexes?
72 supports_index_column_ordering = True
73
74 # Can we roll back DDL in a transaction?
75 can_rollback_ddl = False
76
77 # Does it support operations requiring references rename in a transaction?
78 supports_atomic_references_rename = True
79
80 # Can we issue more than one ALTER COLUMN clause in an ALTER TABLE?
81 supports_combined_alters = False
82
83 # Does it support foreign keys?
84 supports_foreign_keys = True
85
86 # Can an index be renamed?
87 can_rename_index = False
88
89 # Does it support CHECK constraints?
90 supports_column_check_constraints = True
91 supports_table_check_constraints = True
92 # Does the backend support introspection of CHECK constraints?
93 can_introspect_check_constraints = True
94
95 # Does the backend require literal defaults, rather than parameterized ones?
96 requires_literal_defaults = False
97
98 # Suffix for backends that don't support "SELECT xxx;" queries.
99 bare_select_suffix = ""
100
101 # Does the backend support "select for update" queries with limit (and offset)?
102 supports_select_for_update_with_limit = True
103
104 # Does the backend consider table names with different casing to
105 # be equal?
106 ignores_table_name_case = False
107
108 # Does the database support SQL 2003 FILTER (WHERE ...) in aggregate
109 # expressions?
110 supports_aggregate_filter_clause = False
111
112 # Does the backend support window expressions (expression OVER (...))?
113 supports_over_clause = False
114 only_supports_unbounded_with_preceding_and_following = False
115
116 # Does the backend support keyword parameters for cursor.callproc()?
117 supports_callproc_kwargs = False
118
119 # What formats does the backend EXPLAIN syntax support?
120 supported_explain_formats = set()
121
122 # Does the backend support updating rows on constraint or uniqueness errors
123 # during INSERT?
124 supports_update_conflicts = False
125 supports_update_conflicts_with_target = False
126
127 # Does this backend require casting the results of CASE expressions used
128 # in UPDATE statements to ensure the expression has the correct type?
129 requires_casted_case_in_updates = False
130
131 # Does the backend support partial indexes (CREATE INDEX ... WHERE ...)?
132 supports_partial_indexes = True
133 # Does the backend support covering indexes (CREATE INDEX ... INCLUDE ...)?
134 supports_covering_indexes = False
135 # Does the backend support indexes on expressions?
136 supports_expression_indexes = True
137 # Does the backend treat COLLATE as an indexed expression?
138 collate_as_index_expression = False
139
140 # Does the backend support boolean expressions in SELECT and GROUP BY
141 # clauses?
142 supports_boolean_expr_in_select_clause = True
143 # Does the backend support comparing boolean expressions in WHERE clauses?
144 # Eg: WHERE (price > 0) IS NOT NULL
145 supports_comparing_boolean_expr = True
146
147 # Does the backend support JSONField?
148 supports_json_field = True
149 # Can the backend introspect a JSONField?
150 can_introspect_json_field = True
151 # Is there a true datatype for JSON?
152 has_native_json_field = False
153 # Does the backend support __contains and __contained_by lookups for
154 # a JSONField?
155 supports_json_field_contains = True
156 # Does the backend support JSONObject() database function?
157 has_json_object_function = True
158
159 # Does the backend support column collations?
160 supports_collation_on_charfield = True
161 supports_collation_on_textfield = True
162
163 # Does the backend support column and table comments?
164 supports_comments = False
165 # Does the backend support column comments in ADD COLUMN statements?
166 supports_comments_inline = False
167
168 # Does the backend support the logical XOR operator?
169 supports_logical_xor = False
170
171 # Does the backend support unlimited character columns?
172 supports_unlimited_charfield = False
173
174 def __init__(self, connection: BaseDatabaseWrapper):
175 self.connection = connection
176
177 @cached_property
178 def supports_transactions(self) -> bool:
179 """Confirm support for transactions."""
180 with self.connection.cursor() as cursor:
181 cursor.execute("CREATE TABLE ROLLBACK_TEST (X INT)")
182 self.connection.set_autocommit(False)
183 cursor.execute("INSERT INTO ROLLBACK_TEST (X) VALUES (8)")
184 self.connection.rollback()
185 self.connection.set_autocommit(True)
186 cursor.execute("SELECT COUNT(X) FROM ROLLBACK_TEST")
187 row = cursor.fetchone()
188 assert row is not None
189 (count,) = row
190 cursor.execute("DROP TABLE ROLLBACK_TEST")
191 return count == 0