1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223
|
from django.db import DatabaseError, InterfaceError
from django.db.backends.base.features import BaseDatabaseFeatures
from django.db.backends.oracle.oracledb_any import is_oracledb
from django.utils.functional import cached_property
class DatabaseFeatures(BaseDatabaseFeatures):
minimum_database_version = (19,)
# Oracle crashes with "ORA-00932: inconsistent datatypes: expected - got
# BLOB" when grouping by LOBs (#24096).
allows_group_by_lob = False
# Although GROUP BY select index is supported by Oracle 23c+, it requires
# GROUP_BY_POSITION_ENABLED to be enabled to avoid backward compatibility
# issues. Introspection of this settings is not straightforward.
allows_group_by_select_index = False
interprets_empty_strings_as_nulls = True
has_select_for_update = True
has_select_for_update_nowait = True
has_select_for_update_skip_locked = True
has_select_for_update_of = True
select_for_update_of_column = True
can_return_columns_from_insert = True
supports_subqueries_in_group_by = False
ignores_unnecessary_order_by_in_subqueries = False
supports_transactions = True
supports_timezones = False
has_native_duration_field = True
can_defer_constraint_checks = True
supports_partially_nullable_unique_constraints = False
supports_deferrable_unique_constraints = True
truncates_names = True
supports_comments = True
supports_tablespaces = True
supports_sequence_reset = False
can_introspect_materialized_views = True
atomic_transactions = False
nulls_order_largest = True
requires_literal_defaults = True
supports_default_keyword_in_bulk_insert = False
closed_cursor_error_class = InterfaceError
# Select for update with limit can be achieved on Oracle, but not with the
# current backend.
supports_select_for_update_with_limit = False
supports_temporal_subtraction = True
# Oracle doesn't ignore quoted identifiers case but the current backend
# does by uppercasing all identifiers.
ignores_table_name_case = True
supports_index_on_text_field = False
create_test_procedure_without_params_sql = """
CREATE PROCEDURE "TEST_PROCEDURE" AS
V_I INTEGER;
BEGIN
V_I := 1;
END;
"""
create_test_procedure_with_int_param_sql = """
CREATE PROCEDURE "TEST_PROCEDURE" (P_I INTEGER) AS
V_I INTEGER;
BEGIN
V_I := P_I;
END;
"""
supports_callproc_kwargs = True
supports_over_clause = True
supports_frame_range_fixed_distance = True
supports_ignore_conflicts = False
max_query_params = 2**16 - 1
supports_partial_indexes = False
supports_stored_generated_columns = False
supports_virtual_generated_columns = True
can_rename_index = True
supports_slicing_ordering_in_compound = True
requires_compound_order_by_subquery = True
allows_multiple_constraints_on_same_fields = False
supports_json_field_contains = False
supports_collation_on_textfield = False
supports_tuple_lookups = False
test_now_utc_template = "CURRENT_TIMESTAMP AT TIME ZONE 'UTC'"
django_test_expected_failures = {
# A bug in Django/oracledb with respect to string handling (#23843).
"annotations.tests.NonAggregateAnnotationTestCase.test_custom_functions",
"annotations.tests.NonAggregateAnnotationTestCase."
"test_custom_functions_can_ref_other_functions",
}
insert_test_table_with_defaults = (
"INSERT INTO {} VALUES (DEFAULT, DEFAULT, DEFAULT)"
)
@cached_property
def django_test_skips(self):
skips = {
"Oracle doesn't support SHA224.": {
"db_functions.text.test_sha224.SHA224Tests.test_basic",
"db_functions.text.test_sha224.SHA224Tests.test_transform",
},
"Oracle doesn't correctly calculate ISO 8601 week numbering before "
"1583 (the Gregorian calendar was introduced in 1582).": {
"db_functions.datetime.test_extract_trunc.DateFunctionTests."
"test_trunc_week_before_1000",
"db_functions.datetime.test_extract_trunc."
"DateFunctionWithTimeZoneTests.test_trunc_week_before_1000",
},
"Oracle doesn't support bitwise XOR.": {
"expressions.tests.ExpressionOperatorTests.test_lefthand_bitwise_xor",
"expressions.tests.ExpressionOperatorTests."
"test_lefthand_bitwise_xor_null",
"expressions.tests.ExpressionOperatorTests."
"test_lefthand_bitwise_xor_right_null",
},
"Oracle requires ORDER BY in row_number, ANSI:SQL doesn't.": {
"expressions_window.tests.WindowFunctionTests."
"test_row_number_no_ordering",
"prefetch_related.tests.PrefetchLimitTests.test_empty_order",
},
"Oracle doesn't support changing collations on indexed columns (#33671).": {
"migrations.test_operations.OperationTests."
"test_alter_field_pk_fk_db_collation",
},
"Oracle doesn't support comparing NCLOB to NUMBER.": {
"generic_relations_regress.tests.GenericRelationTests."
"test_textlink_filter",
},
"Oracle doesn't support casting filters to NUMBER.": {
"lookup.tests.LookupQueryingTests.test_aggregate_combined_lookup",
},
}
if self.connection.oracle_version < (23,):
skips.update(
{
"Raises ORA-00600 on Oracle < 23c: internal error code.": {
"model_fields.test_jsonfield.TestQuerying."
"test_usage_in_subquery",
},
}
)
if self.connection.is_pool:
skips.update(
{
"Pooling does not support persistent connections": {
"backends.base.test_base.ConnectionHealthChecksTests."
"test_health_checks_enabled",
"backends.base.test_base.ConnectionHealthChecksTests."
"test_health_checks_enabled_errors_occurred",
"backends.base.test_base.ConnectionHealthChecksTests."
"test_health_checks_disabled",
"backends.base.test_base.ConnectionHealthChecksTests."
"test_set_autocommit_health_checks_enabled",
"servers.tests.LiveServerTestCloseConnectionTest."
"test_closes_connections",
"backends.oracle.tests.TransactionalTests."
"test_password_with_at_sign",
},
}
)
if is_oracledb and self.connection.oracledb_version >= (2, 1, 2):
skips.update(
{
"python-oracledb 2.1.2+ no longer hides 'ORA-1403: no data found' "
"exceptions raised in database triggers.": {
"backends.oracle.tests.TransactionalTests."
"test_hidden_no_data_found_exception"
},
},
)
return skips
@cached_property
def introspected_field_types(self):
return {
**super().introspected_field_types,
"GenericIPAddressField": "CharField",
"PositiveBigIntegerField": "BigIntegerField",
"PositiveIntegerField": "IntegerField",
"PositiveSmallIntegerField": "IntegerField",
"SmallIntegerField": "IntegerField",
"TimeField": "DateTimeField",
}
@cached_property
def test_collations(self):
return {
"ci": "BINARY_CI",
"cs": "BINARY",
"non_default": "SWEDISH_CI",
"swedish_ci": "SWEDISH_CI",
"virtual": "SWEDISH_CI" if self.supports_collation_on_charfield else None,
}
@cached_property
def supports_collation_on_charfield(self):
sql = "SELECT CAST('a' AS VARCHAR2(4001))" + self.bare_select_suffix
with self.connection.cursor() as cursor:
try:
cursor.execute(sql)
except DatabaseError as e:
if e.args[0].code == 910:
return False
raise
return True
@cached_property
def supports_primitives_in_json_field(self):
return self.connection.oracle_version >= (21,)
@cached_property
def supports_frame_exclusion(self):
return self.connection.oracle_version >= (21,)
@cached_property
def supports_boolean_expr_in_select_clause(self):
return self.connection.oracle_version >= (23,)
@cached_property
def supports_comparing_boolean_expr(self):
return self.connection.oracle_version >= (23,)
@cached_property
def supports_aggregation_over_interval_types(self):
return self.connection.oracle_version >= (23,)
@cached_property
def bare_select_suffix(self):
return "" if self.connection.oracle_version >= (23,) else " FROM DUAL"
|