operations.py 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631
  1. import datetime
  2. import re
  3. import uuid
  4. from functools import lru_cache
  5. from django.conf import settings
  6. from django.db.backends.base.operations import BaseDatabaseOperations
  7. from django.db.backends.utils import strip_quotes, truncate_name
  8. from django.db.models.expressions import Exists, ExpressionWrapper, RawSQL
  9. from django.db.models.query_utils import Q
  10. from django.db.utils import DatabaseError
  11. from django.utils import timezone
  12. from django.utils.encoding import force_bytes, force_str
  13. from django.utils.functional import cached_property
  14. from .base import Database
  15. from .utils import BulkInsertMapper, InsertVar, Oracle_datetime
  16. class DatabaseOperations(BaseDatabaseOperations):
  17. # Oracle uses NUMBER(5), NUMBER(11), and NUMBER(19) for integer fields.
  18. # SmallIntegerField uses NUMBER(11) instead of NUMBER(5), which is used by
  19. # SmallAutoField, to preserve backward compatibility.
  20. integer_field_ranges = {
  21. 'SmallIntegerField': (-99999999999, 99999999999),
  22. 'IntegerField': (-99999999999, 99999999999),
  23. 'BigIntegerField': (-9999999999999999999, 9999999999999999999),
  24. 'PositiveSmallIntegerField': (0, 99999999999),
  25. 'PositiveIntegerField': (0, 99999999999),
  26. 'SmallAutoField': (-99999, 99999),
  27. 'AutoField': (-99999999999, 99999999999),
  28. 'BigAutoField': (-9999999999999999999, 9999999999999999999),
  29. }
  30. set_operators = {**BaseDatabaseOperations.set_operators, 'difference': 'MINUS'}
  31. # TODO: colorize this SQL code with style.SQL_KEYWORD(), etc.
  32. _sequence_reset_sql = """
  33. DECLARE
  34. table_value integer;
  35. seq_value integer;
  36. seq_name user_tab_identity_cols.sequence_name%%TYPE;
  37. BEGIN
  38. BEGIN
  39. SELECT sequence_name INTO seq_name FROM user_tab_identity_cols
  40. WHERE table_name = '%(table_name)s' AND
  41. column_name = '%(column_name)s';
  42. EXCEPTION WHEN NO_DATA_FOUND THEN
  43. seq_name := '%(no_autofield_sequence_name)s';
  44. END;
  45. SELECT NVL(MAX(%(column)s), 0) INTO table_value FROM %(table)s;
  46. SELECT NVL(last_number - cache_size, 0) INTO seq_value FROM user_sequences
  47. WHERE sequence_name = seq_name;
  48. WHILE table_value > seq_value LOOP
  49. EXECUTE IMMEDIATE 'SELECT "'||seq_name||'".nextval FROM DUAL'
  50. INTO seq_value;
  51. END LOOP;
  52. END;
  53. /"""
  54. # Oracle doesn't support string without precision; use the max string size.
  55. cast_char_field_without_max_length = 'NVARCHAR2(2000)'
  56. cast_data_types = {
  57. 'AutoField': 'NUMBER(11)',
  58. 'BigAutoField': 'NUMBER(19)',
  59. 'SmallAutoField': 'NUMBER(5)',
  60. 'TextField': cast_char_field_without_max_length,
  61. }
  62. def cache_key_culling_sql(self):
  63. return 'SELECT cache_key FROM %s ORDER BY cache_key OFFSET %%s ROWS FETCH FIRST 1 ROWS ONLY'
  64. def date_extract_sql(self, lookup_type, field_name):
  65. if lookup_type == 'week_day':
  66. # TO_CHAR(field, 'D') returns an integer from 1-7, where 1=Sunday.
  67. return "TO_CHAR(%s, 'D')" % field_name
  68. elif lookup_type == 'week':
  69. # IW = ISO week number
  70. return "TO_CHAR(%s, 'IW')" % field_name
  71. elif lookup_type == 'quarter':
  72. return "TO_CHAR(%s, 'Q')" % field_name
  73. elif lookup_type == 'iso_year':
  74. return "TO_CHAR(%s, 'IYYY')" % field_name
  75. else:
  76. # https://docs.oracle.com/en/database/oracle/oracle-database/18/sqlrf/EXTRACT-datetime.html
  77. return "EXTRACT(%s FROM %s)" % (lookup_type.upper(), field_name)
  78. def date_trunc_sql(self, lookup_type, field_name):
  79. # https://docs.oracle.com/en/database/oracle/oracle-database/18/sqlrf/ROUND-and-TRUNC-Date-Functions.html
  80. if lookup_type in ('year', 'month'):
  81. return "TRUNC(%s, '%s')" % (field_name, lookup_type.upper())
  82. elif lookup_type == 'quarter':
  83. return "TRUNC(%s, 'Q')" % field_name
  84. elif lookup_type == 'week':
  85. return "TRUNC(%s, 'IW')" % field_name
  86. else:
  87. return "TRUNC(%s)" % field_name
  88. # Oracle crashes with "ORA-03113: end-of-file on communication channel"
  89. # if the time zone name is passed in parameter. Use interpolation instead.
  90. # https://groups.google.com/forum/#!msg/django-developers/zwQju7hbG78/9l934yelwfsJ
  91. # This regexp matches all time zone names from the zoneinfo database.
  92. _tzname_re = re.compile(r'^[\w/:+-]+$')
  93. def _prepare_tzname_delta(self, tzname):
  94. if '+' in tzname:
  95. return tzname[tzname.find('+'):]
  96. elif '-' in tzname:
  97. return tzname[tzname.find('-'):]
  98. return tzname
  99. def _convert_field_to_tz(self, field_name, tzname):
  100. if not settings.USE_TZ:
  101. return field_name
  102. if not self._tzname_re.match(tzname):
  103. raise ValueError("Invalid time zone name: %s" % tzname)
  104. # Convert from connection timezone to the local time, returning
  105. # TIMESTAMP WITH TIME ZONE and cast it back to TIMESTAMP to strip the
  106. # TIME ZONE details.
  107. if self.connection.timezone_name != tzname:
  108. return "CAST((FROM_TZ(%s, '%s') AT TIME ZONE '%s') AS TIMESTAMP)" % (
  109. field_name,
  110. self.connection.timezone_name,
  111. self._prepare_tzname_delta(tzname),
  112. )
  113. return field_name
  114. def datetime_cast_date_sql(self, field_name, tzname):
  115. field_name = self._convert_field_to_tz(field_name, tzname)
  116. return 'TRUNC(%s)' % field_name
  117. def datetime_cast_time_sql(self, field_name, tzname):
  118. # Since `TimeField` values are stored as TIMESTAMP where only the date
  119. # part is ignored, convert the field to the specified timezone.
  120. return self._convert_field_to_tz(field_name, tzname)
  121. def datetime_extract_sql(self, lookup_type, field_name, tzname):
  122. field_name = self._convert_field_to_tz(field_name, tzname)
  123. return self.date_extract_sql(lookup_type, field_name)
  124. def datetime_trunc_sql(self, lookup_type, field_name, tzname):
  125. field_name = self._convert_field_to_tz(field_name, tzname)
  126. # https://docs.oracle.com/en/database/oracle/oracle-database/18/sqlrf/ROUND-and-TRUNC-Date-Functions.html
  127. if lookup_type in ('year', 'month'):
  128. sql = "TRUNC(%s, '%s')" % (field_name, lookup_type.upper())
  129. elif lookup_type == 'quarter':
  130. sql = "TRUNC(%s, 'Q')" % field_name
  131. elif lookup_type == 'week':
  132. sql = "TRUNC(%s, 'IW')" % field_name
  133. elif lookup_type == 'day':
  134. sql = "TRUNC(%s)" % field_name
  135. elif lookup_type == 'hour':
  136. sql = "TRUNC(%s, 'HH24')" % field_name
  137. elif lookup_type == 'minute':
  138. sql = "TRUNC(%s, 'MI')" % field_name
  139. else:
  140. sql = "CAST(%s AS DATE)" % field_name # Cast to DATE removes sub-second precision.
  141. return sql
  142. def time_trunc_sql(self, lookup_type, field_name):
  143. # The implementation is similar to `datetime_trunc_sql` as both
  144. # `DateTimeField` and `TimeField` are stored as TIMESTAMP where
  145. # the date part of the later is ignored.
  146. if lookup_type == 'hour':
  147. sql = "TRUNC(%s, 'HH24')" % field_name
  148. elif lookup_type == 'minute':
  149. sql = "TRUNC(%s, 'MI')" % field_name
  150. elif lookup_type == 'second':
  151. sql = "CAST(%s AS DATE)" % field_name # Cast to DATE removes sub-second precision.
  152. return sql
  153. def get_db_converters(self, expression):
  154. converters = super().get_db_converters(expression)
  155. internal_type = expression.output_field.get_internal_type()
  156. if internal_type == 'TextField':
  157. converters.append(self.convert_textfield_value)
  158. elif internal_type == 'BinaryField':
  159. converters.append(self.convert_binaryfield_value)
  160. elif internal_type in ['BooleanField', 'NullBooleanField']:
  161. converters.append(self.convert_booleanfield_value)
  162. elif internal_type == 'DateTimeField':
  163. if settings.USE_TZ:
  164. converters.append(self.convert_datetimefield_value)
  165. elif internal_type == 'DateField':
  166. converters.append(self.convert_datefield_value)
  167. elif internal_type == 'TimeField':
  168. converters.append(self.convert_timefield_value)
  169. elif internal_type == 'UUIDField':
  170. converters.append(self.convert_uuidfield_value)
  171. # Oracle stores empty strings as null. If the field accepts the empty
  172. # string, undo this to adhere to the Django convention of using
  173. # the empty string instead of null.
  174. if expression.field.empty_strings_allowed:
  175. converters.append(
  176. self.convert_empty_bytes
  177. if internal_type == 'BinaryField' else
  178. self.convert_empty_string
  179. )
  180. return converters
  181. def convert_textfield_value(self, value, expression, connection):
  182. if isinstance(value, Database.LOB):
  183. value = value.read()
  184. return value
  185. def convert_binaryfield_value(self, value, expression, connection):
  186. if isinstance(value, Database.LOB):
  187. value = force_bytes(value.read())
  188. return value
  189. def convert_booleanfield_value(self, value, expression, connection):
  190. if value in (0, 1):
  191. value = bool(value)
  192. return value
  193. # cx_Oracle always returns datetime.datetime objects for
  194. # DATE and TIMESTAMP columns, but Django wants to see a
  195. # python datetime.date, .time, or .datetime.
  196. def convert_datetimefield_value(self, value, expression, connection):
  197. if value is not None:
  198. value = timezone.make_aware(value, self.connection.timezone)
  199. return value
  200. def convert_datefield_value(self, value, expression, connection):
  201. if isinstance(value, Database.Timestamp):
  202. value = value.date()
  203. return value
  204. def convert_timefield_value(self, value, expression, connection):
  205. if isinstance(value, Database.Timestamp):
  206. value = value.time()
  207. return value
  208. def convert_uuidfield_value(self, value, expression, connection):
  209. if value is not None:
  210. value = uuid.UUID(value)
  211. return value
  212. @staticmethod
  213. def convert_empty_string(value, expression, connection):
  214. return '' if value is None else value
  215. @staticmethod
  216. def convert_empty_bytes(value, expression, connection):
  217. return b'' if value is None else value
  218. def deferrable_sql(self):
  219. return " DEFERRABLE INITIALLY DEFERRED"
  220. def fetch_returned_insert_columns(self, cursor):
  221. value = cursor._insert_id_var.getvalue()
  222. if value is None or value == []:
  223. # cx_Oracle < 6.3 returns None, >= 6.3 returns empty list.
  224. raise DatabaseError(
  225. 'The database did not return a new row id. Probably "ORA-1403: '
  226. 'no data found" was raised internally but was hidden by the '
  227. 'Oracle OCI library (see https://code.djangoproject.com/ticket/28859).'
  228. )
  229. # cx_Oracle < 7 returns value, >= 7 returns list with single value.
  230. return value if isinstance(value, list) else [value]
  231. def field_cast_sql(self, db_type, internal_type):
  232. if db_type and db_type.endswith('LOB'):
  233. return "DBMS_LOB.SUBSTR(%s)"
  234. else:
  235. return "%s"
  236. def no_limit_value(self):
  237. return None
  238. def limit_offset_sql(self, low_mark, high_mark):
  239. fetch, offset = self._get_limit_offset_params(low_mark, high_mark)
  240. return ' '.join(sql for sql in (
  241. ('OFFSET %d ROWS' % offset) if offset else None,
  242. ('FETCH FIRST %d ROWS ONLY' % fetch) if fetch else None,
  243. ) if sql)
  244. def last_executed_query(self, cursor, sql, params):
  245. # https://cx-oracle.readthedocs.io/en/latest/cursor.html#Cursor.statement
  246. # The DB API definition does not define this attribute.
  247. statement = cursor.statement
  248. # Unlike Psycopg's `query` and MySQLdb`'s `_executed`, cx_Oracle's
  249. # `statement` doesn't contain the query parameters. Substitute
  250. # parameters manually.
  251. if isinstance(params, (tuple, list)):
  252. for i, param in enumerate(params):
  253. statement = statement.replace(':arg%d' % i, force_str(param, errors='replace'))
  254. elif isinstance(params, dict):
  255. for key, param in params.items():
  256. statement = statement.replace(':%s' % key, force_str(param, errors='replace'))
  257. return statement
  258. def last_insert_id(self, cursor, table_name, pk_name):
  259. sq_name = self._get_sequence_name(cursor, strip_quotes(table_name), pk_name)
  260. cursor.execute('"%s".currval' % sq_name)
  261. return cursor.fetchone()[0]
  262. def lookup_cast(self, lookup_type, internal_type=None):
  263. if lookup_type in ('iexact', 'icontains', 'istartswith', 'iendswith'):
  264. return "UPPER(%s)"
  265. return "%s"
  266. def max_in_list_size(self):
  267. return 1000
  268. def max_name_length(self):
  269. return 30
  270. def pk_default_value(self):
  271. return "NULL"
  272. def prep_for_iexact_query(self, x):
  273. return x
  274. def process_clob(self, value):
  275. if value is None:
  276. return ''
  277. return value.read()
  278. def quote_name(self, name):
  279. # SQL92 requires delimited (quoted) names to be case-sensitive. When
  280. # not quoted, Oracle has case-insensitive behavior for identifiers, but
  281. # always defaults to uppercase.
  282. # We simplify things by making Oracle identifiers always uppercase.
  283. if not name.startswith('"') and not name.endswith('"'):
  284. name = '"%s"' % truncate_name(name.upper(), self.max_name_length())
  285. # Oracle puts the query text into a (query % args) construct, so % signs
  286. # in names need to be escaped. The '%%' will be collapsed back to '%' at
  287. # that stage so we aren't really making the name longer here.
  288. name = name.replace('%', '%%')
  289. return name.upper()
  290. def random_function_sql(self):
  291. return "DBMS_RANDOM.RANDOM"
  292. def regex_lookup(self, lookup_type):
  293. if lookup_type == 'regex':
  294. match_option = "'c'"
  295. else:
  296. match_option = "'i'"
  297. return 'REGEXP_LIKE(%%s, %%s, %s)' % match_option
  298. def return_insert_columns(self, fields):
  299. if not fields:
  300. return '', ()
  301. sql = 'RETURNING %s.%s INTO %%s' % (
  302. self.quote_name(fields[0].model._meta.db_table),
  303. self.quote_name(fields[0].column),
  304. )
  305. return sql, (InsertVar(fields[0]),)
  306. def __foreign_key_constraints(self, table_name, recursive):
  307. with self.connection.cursor() as cursor:
  308. if recursive:
  309. cursor.execute("""
  310. SELECT
  311. user_tables.table_name, rcons.constraint_name
  312. FROM
  313. user_tables
  314. JOIN
  315. user_constraints cons
  316. ON (user_tables.table_name = cons.table_name AND cons.constraint_type = ANY('P', 'U'))
  317. LEFT JOIN
  318. user_constraints rcons
  319. ON (user_tables.table_name = rcons.table_name AND rcons.constraint_type = 'R')
  320. START WITH user_tables.table_name = UPPER(%s)
  321. CONNECT BY NOCYCLE PRIOR cons.constraint_name = rcons.r_constraint_name
  322. GROUP BY
  323. user_tables.table_name, rcons.constraint_name
  324. HAVING user_tables.table_name != UPPER(%s)
  325. ORDER BY MAX(level) DESC
  326. """, (table_name, table_name))
  327. else:
  328. cursor.execute("""
  329. SELECT
  330. cons.table_name, cons.constraint_name
  331. FROM
  332. user_constraints cons
  333. WHERE
  334. cons.constraint_type = 'R'
  335. AND cons.table_name = UPPER(%s)
  336. """, (table_name,))
  337. return cursor.fetchall()
  338. @cached_property
  339. def _foreign_key_constraints(self):
  340. # 512 is large enough to fit the ~330 tables (as of this writing) in
  341. # Django's test suite.
  342. return lru_cache(maxsize=512)(self.__foreign_key_constraints)
  343. def sql_flush(self, style, tables, sequences, allow_cascade=False):
  344. if tables:
  345. truncated_tables = {table.upper() for table in tables}
  346. constraints = set()
  347. # Oracle's TRUNCATE CASCADE only works with ON DELETE CASCADE
  348. # foreign keys which Django doesn't define. Emulate the
  349. # PostgreSQL behavior which truncates all dependent tables by
  350. # manually retrieving all foreign key constraints and resolving
  351. # dependencies.
  352. for table in tables:
  353. for foreign_table, constraint in self._foreign_key_constraints(table, recursive=allow_cascade):
  354. if allow_cascade:
  355. truncated_tables.add(foreign_table)
  356. constraints.add((foreign_table, constraint))
  357. sql = [
  358. "%s %s %s %s %s %s %s %s;" % (
  359. style.SQL_KEYWORD('ALTER'),
  360. style.SQL_KEYWORD('TABLE'),
  361. style.SQL_FIELD(self.quote_name(table)),
  362. style.SQL_KEYWORD('DISABLE'),
  363. style.SQL_KEYWORD('CONSTRAINT'),
  364. style.SQL_FIELD(self.quote_name(constraint)),
  365. style.SQL_KEYWORD('KEEP'),
  366. style.SQL_KEYWORD('INDEX'),
  367. ) for table, constraint in constraints
  368. ] + [
  369. "%s %s %s;" % (
  370. style.SQL_KEYWORD('TRUNCATE'),
  371. style.SQL_KEYWORD('TABLE'),
  372. style.SQL_FIELD(self.quote_name(table)),
  373. ) for table in truncated_tables
  374. ] + [
  375. "%s %s %s %s %s %s;" % (
  376. style.SQL_KEYWORD('ALTER'),
  377. style.SQL_KEYWORD('TABLE'),
  378. style.SQL_FIELD(self.quote_name(table)),
  379. style.SQL_KEYWORD('ENABLE'),
  380. style.SQL_KEYWORD('CONSTRAINT'),
  381. style.SQL_FIELD(self.quote_name(constraint)),
  382. ) for table, constraint in constraints
  383. ]
  384. # Since we've just deleted all the rows, running our sequence
  385. # ALTER code will reset the sequence to 0.
  386. sql.extend(self.sequence_reset_by_name_sql(style, sequences))
  387. return sql
  388. else:
  389. return []
  390. def sequence_reset_by_name_sql(self, style, sequences):
  391. sql = []
  392. for sequence_info in sequences:
  393. no_autofield_sequence_name = self._get_no_autofield_sequence_name(sequence_info['table'])
  394. table = self.quote_name(sequence_info['table'])
  395. column = self.quote_name(sequence_info['column'] or 'id')
  396. query = self._sequence_reset_sql % {
  397. 'no_autofield_sequence_name': no_autofield_sequence_name,
  398. 'table': table,
  399. 'column': column,
  400. 'table_name': strip_quotes(table),
  401. 'column_name': strip_quotes(column),
  402. }
  403. sql.append(query)
  404. return sql
  405. def sequence_reset_sql(self, style, model_list):
  406. from django.db import models
  407. output = []
  408. query = self._sequence_reset_sql
  409. for model in model_list:
  410. for f in model._meta.local_fields:
  411. if isinstance(f, models.AutoField):
  412. no_autofield_sequence_name = self._get_no_autofield_sequence_name(model._meta.db_table)
  413. table = self.quote_name(model._meta.db_table)
  414. column = self.quote_name(f.column)
  415. output.append(query % {
  416. 'no_autofield_sequence_name': no_autofield_sequence_name,
  417. 'table': table,
  418. 'column': column,
  419. 'table_name': strip_quotes(table),
  420. 'column_name': strip_quotes(column),
  421. })
  422. # Only one AutoField is allowed per model, so don't
  423. # continue to loop
  424. break
  425. for f in model._meta.many_to_many:
  426. if not f.remote_field.through:
  427. no_autofield_sequence_name = self._get_no_autofield_sequence_name(f.m2m_db_table())
  428. table = self.quote_name(f.m2m_db_table())
  429. column = self.quote_name('id')
  430. output.append(query % {
  431. 'no_autofield_sequence_name': no_autofield_sequence_name,
  432. 'table': table,
  433. 'column': column,
  434. 'table_name': strip_quotes(table),
  435. 'column_name': 'ID',
  436. })
  437. return output
  438. def start_transaction_sql(self):
  439. return ''
  440. def tablespace_sql(self, tablespace, inline=False):
  441. if inline:
  442. return "USING INDEX TABLESPACE %s" % self.quote_name(tablespace)
  443. else:
  444. return "TABLESPACE %s" % self.quote_name(tablespace)
  445. def adapt_datefield_value(self, value):
  446. """
  447. Transform a date value to an object compatible with what is expected
  448. by the backend driver for date columns.
  449. The default implementation transforms the date to text, but that is not
  450. necessary for Oracle.
  451. """
  452. return value
  453. def adapt_datetimefield_value(self, value):
  454. """
  455. Transform a datetime value to an object compatible with what is expected
  456. by the backend driver for datetime columns.
  457. If naive datetime is passed assumes that is in UTC. Normally Django
  458. models.DateTimeField makes sure that if USE_TZ is True passed datetime
  459. is timezone aware.
  460. """
  461. if value is None:
  462. return None
  463. # Expression values are adapted by the database.
  464. if hasattr(value, 'resolve_expression'):
  465. return value
  466. # cx_Oracle doesn't support tz-aware datetimes
  467. if timezone.is_aware(value):
  468. if settings.USE_TZ:
  469. value = timezone.make_naive(value, self.connection.timezone)
  470. else:
  471. raise ValueError("Oracle backend does not support timezone-aware datetimes when USE_TZ is False.")
  472. return Oracle_datetime.from_datetime(value)
  473. def adapt_timefield_value(self, value):
  474. if value is None:
  475. return None
  476. # Expression values are adapted by the database.
  477. if hasattr(value, 'resolve_expression'):
  478. return value
  479. if isinstance(value, str):
  480. return datetime.datetime.strptime(value, '%H:%M:%S')
  481. # Oracle doesn't support tz-aware times
  482. if timezone.is_aware(value):
  483. raise ValueError("Oracle backend does not support timezone-aware times.")
  484. return Oracle_datetime(1900, 1, 1, value.hour, value.minute,
  485. value.second, value.microsecond)
  486. def combine_expression(self, connector, sub_expressions):
  487. lhs, rhs = sub_expressions
  488. if connector == '%%':
  489. return 'MOD(%s)' % ','.join(sub_expressions)
  490. elif connector == '&':
  491. return 'BITAND(%s)' % ','.join(sub_expressions)
  492. elif connector == '|':
  493. return 'BITAND(-%(lhs)s-1,%(rhs)s)+%(lhs)s' % {'lhs': lhs, 'rhs': rhs}
  494. elif connector == '<<':
  495. return '(%(lhs)s * POWER(2, %(rhs)s))' % {'lhs': lhs, 'rhs': rhs}
  496. elif connector == '>>':
  497. return 'FLOOR(%(lhs)s / POWER(2, %(rhs)s))' % {'lhs': lhs, 'rhs': rhs}
  498. elif connector == '^':
  499. return 'POWER(%s)' % ','.join(sub_expressions)
  500. return super().combine_expression(connector, sub_expressions)
  501. def _get_no_autofield_sequence_name(self, table):
  502. """
  503. Manually created sequence name to keep backward compatibility for
  504. AutoFields that aren't Oracle identity columns.
  505. """
  506. name_length = self.max_name_length() - 3
  507. return '%s_SQ' % truncate_name(strip_quotes(table), name_length).upper()
  508. def _get_sequence_name(self, cursor, table, pk_name):
  509. cursor.execute("""
  510. SELECT sequence_name
  511. FROM user_tab_identity_cols
  512. WHERE table_name = UPPER(%s)
  513. AND column_name = UPPER(%s)""", [table, pk_name])
  514. row = cursor.fetchone()
  515. return self._get_no_autofield_sequence_name(table) if row is None else row[0]
  516. def bulk_insert_sql(self, fields, placeholder_rows):
  517. query = []
  518. for row in placeholder_rows:
  519. select = []
  520. for i, placeholder in enumerate(row):
  521. # A model without any fields has fields=[None].
  522. if fields[i]:
  523. internal_type = getattr(fields[i], 'target_field', fields[i]).get_internal_type()
  524. placeholder = BulkInsertMapper.types.get(internal_type, '%s') % placeholder
  525. # Add columns aliases to the first select to avoid "ORA-00918:
  526. # column ambiguously defined" when two or more columns in the
  527. # first select have the same value.
  528. if not query:
  529. placeholder = '%s col_%s' % (placeholder, i)
  530. select.append(placeholder)
  531. query.append('SELECT %s FROM DUAL' % ', '.join(select))
  532. # Bulk insert to tables with Oracle identity columns causes Oracle to
  533. # add sequence.nextval to it. Sequence.nextval cannot be used with the
  534. # UNION operator. To prevent incorrect SQL, move UNION to a subquery.
  535. return 'SELECT * FROM (%s)' % ' UNION ALL '.join(query)
  536. def subtract_temporals(self, internal_type, lhs, rhs):
  537. if internal_type == 'DateField':
  538. lhs_sql, lhs_params = lhs
  539. rhs_sql, rhs_params = rhs
  540. params = (*lhs_params, *rhs_params)
  541. return "NUMTODSINTERVAL(TO_NUMBER(%s - %s), 'DAY')" % (lhs_sql, rhs_sql), params
  542. return super().subtract_temporals(internal_type, lhs, rhs)
  543. def bulk_batch_size(self, fields, objs):
  544. """Oracle restricts the number of parameters in a query."""
  545. if fields:
  546. return self.connection.features.max_query_params // len(fields)
  547. return len(objs)
  548. def conditional_expression_supported_in_where_clause(self, expression):
  549. """
  550. Oracle supports only EXISTS(...) or filters in the WHERE clause, others
  551. must be compared with True.
  552. """
  553. if isinstance(expression, Exists):
  554. return True
  555. if isinstance(expression, ExpressionWrapper) and isinstance(expression.expression, Q):
  556. return True
  557. if isinstance(expression, RawSQL) and expression.conditional:
  558. return True
  559. return False