Plain is headed towards 1.0! Subscribe for development updates →

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