codekingpro/portable-devtools
114k
1# dialects/oracle/base.py
2# Copyright (C) 2005-2024 the SQLAlchemy authors and contributors
3# <see AUTHORS file>
4#
5# This module is part of SQLAlchemy and is released under
6# the MIT License: https://www.opensource.org/licenses/mit-license.php
7# mypy: ignore-errors
8
9
10r"""
11.. dialect:: oracle
12 :name: Oracle
13 :full_support: 18c
14 :normal_support: 11+
15 :best_effort: 9+
16
17
18Auto Increment Behavior
19-----------------------
20
21SQLAlchemy Table objects which include integer primary keys are usually
22assumed to have "autoincrementing" behavior, meaning they can generate their
23own primary key values upon INSERT. For use within Oracle, two options are
24available, which are the use of IDENTITY columns (Oracle 12 and above only)
25or the association of a SEQUENCE with the column.
26
27Specifying GENERATED AS IDENTITY (Oracle 12 and above)
28~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
29
30Starting from version 12 Oracle can make use of identity columns using
31the :class:`_sql.Identity` to specify the autoincrementing behavior::
32
33 t = Table('mytable', metadata,
34 Column('id', Integer, Identity(start=3), primary_key=True),
35 Column(...), ...
36 )
37
38The CREATE TABLE for the above :class:`_schema.Table` object would be:
39
40.. sourcecode:: sql
41
42 CREATE TABLE mytable (
43 id INTEGER GENERATED BY DEFAULT AS IDENTITY (START WITH 3),
44 ...,
45 PRIMARY KEY (id)
46 )
47
48The :class:`_schema.Identity` object support many options to control the
49"autoincrementing" behavior of the column, like the starting value, the
50incrementing value, etc.
51In addition to the standard options, Oracle supports setting
52:paramref:`_schema.Identity.always` to ``None`` to use the default
53generated mode, rendering GENERATED AS IDENTITY in the DDL. It also supports
54setting :paramref:`_schema.Identity.on_null` to ``True`` to specify ON NULL
55in conjunction with a 'BY DEFAULT' identity column.
56
57Using a SEQUENCE (all Oracle versions)
58~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
59
60Older version of Oracle had no "autoincrement"
61feature, SQLAlchemy relies upon sequences to produce these values. With the
62older Oracle versions, *a sequence must always be explicitly specified to
63enable autoincrement*. This is divergent with the majority of documentation
64examples which assume the usage of an autoincrement-capable database. To
65specify sequences, use the sqlalchemy.schema.Sequence object which is passed
66to a Column construct::
67
68 t = Table('mytable', metadata,
69 Column('id', Integer, Sequence('id_seq', start=1), primary_key=True),
70 Column(...), ...
71 )
72
73This step is also required when using table reflection, i.e. autoload_with=engine::
74
75 t = Table('mytable', metadata,
76 Column('id', Integer, Sequence('id_seq', start=1), primary_key=True),
77 autoload_with=engine
78 )
79
80.. versionchanged:: 1.4 Added :class:`_schema.Identity` construct
81 in a :class:`_schema.Column` to specify the option of an autoincrementing
82 column.
83
84.. _oracle_isolation_level:
85
86Transaction Isolation Level / Autocommit
87----------------------------------------
88
89The Oracle database supports "READ COMMITTED" and "SERIALIZABLE" modes of
90isolation. The AUTOCOMMIT isolation level is also supported by the cx_Oracle
91dialect.
92
93To set using per-connection execution options::
94
95 connection = engine.connect()
96 connection = connection.execution_options(
97 isolation_level="AUTOCOMMIT"
98 )
99
100For ``READ COMMITTED`` and ``SERIALIZABLE``, the Oracle dialect sets the
101level at the session level using ``ALTER SESSION``, which is reverted back
102to its default setting when the connection is returned to the connection
103pool.
104
105Valid values for ``isolation_level`` include:
106
107* ``READ COMMITTED``
108* ``AUTOCOMMIT``
109* ``SERIALIZABLE``
110
111.. note:: The implementation for the
112 :meth:`_engine.Connection.get_isolation_level` method as implemented by the
113 Oracle dialect necessarily forces the start of a transaction using the
114 Oracle LOCAL_TRANSACTION_ID function; otherwise no level is normally
115 readable.
116
117 Additionally, the :meth:`_engine.Connection.get_isolation_level` method will
118 raise an exception if the ``v$transaction`` view is not available due to
119 permissions or other reasons, which is a common occurrence in Oracle
120 installations.
121
122 The cx_Oracle dialect attempts to call the
123 :meth:`_engine.Connection.get_isolation_level` method when the dialect makes
124 its first connection to the database in order to acquire the
125 "default"isolation level. This default level is necessary so that the level
126 can be reset on a connection after it has been temporarily modified using
127 :meth:`_engine.Connection.execution_options` method. In the common event
128 that the :meth:`_engine.Connection.get_isolation_level` method raises an
129 exception due to ``v$transaction`` not being readable as well as any other
130 database-related failure, the level is assumed to be "READ COMMITTED". No
131 warning is emitted for this initial first-connect condition as it is
132 expected to be a common restriction on Oracle databases.
133
134.. versionadded:: 1.3.16 added support for AUTOCOMMIT to the cx_oracle dialect
135 as well as the notion of a default isolation level
136
137.. versionadded:: 1.3.21 Added support for SERIALIZABLE as well as live
138 reading of the isolation level.
139
140.. versionchanged:: 1.3.22 In the event that the default isolation
141 level cannot be read due to permissions on the v$transaction view as
142 is common in Oracle installations, the default isolation level is hardcoded
143 to "READ COMMITTED" which was the behavior prior to 1.3.21.
144
145.. seealso::
146
147 :ref:`dbapi_autocommit`
148
149Identifier Casing
150-----------------
151
152In Oracle, the data dictionary represents all case insensitive identifier
153names using UPPERCASE text. SQLAlchemy on the other hand considers an
154all-lower case identifier name to be case insensitive. The Oracle dialect
155converts all case insensitive identifiers to and from those two formats during
156schema level communication, such as reflection of tables and indexes. Using
157an UPPERCASE name on the SQLAlchemy side indicates a case sensitive
158identifier, and SQLAlchemy will quote the name - this will cause mismatches
159against data dictionary data received from Oracle, so unless identifier names
160have been truly created as case sensitive (i.e. using quoted names), all
161lowercase names should be used on the SQLAlchemy side.
162
163.. _oracle_max_identifier_lengths:
164
165Max Identifier Lengths
166----------------------
167
168Oracle has changed the default max identifier length as of Oracle Server
169version 12.2. Prior to this version, the length was 30, and for 12.2 and
170greater it is now 128. This change impacts SQLAlchemy in the area of
171generated SQL label names as well as the generation of constraint names,
172particularly in the case where the constraint naming convention feature
173described at :ref:`constraint_naming_conventions` is being used.
174
175To assist with this change and others, Oracle includes the concept of a
176"compatibility" version, which is a version number that is independent of the
177actual server version in order to assist with migration of Oracle databases,
178and may be configured within the Oracle server itself. This compatibility
179version is retrieved using the query ``SELECT value FROM v$parameter WHERE
180name = 'compatible';``. The SQLAlchemy Oracle dialect, when tasked with
181determining the default max identifier length, will attempt to use this query
182upon first connect in order to determine the effective compatibility version of
183the server, which determines what the maximum allowed identifier length is for
184the server. If the table is not available, the server version information is
185used instead.
186
187As of SQLAlchemy 1.4, the default max identifier length for the Oracle dialect
188is 128 characters. Upon first connect, the compatibility version is detected
189and if it is less than Oracle version 12.2, the max identifier length is
190changed to be 30 characters. In all cases, setting the
191:paramref:`_sa.create_engine.max_identifier_length` parameter will bypass this
192change and the value given will be used as is::
193
194 engine = create_engine(
195 "oracle+cx_oracle://scott:tiger@oracle122",
196 max_identifier_length=30)
197
198The maximum identifier length comes into play both when generating anonymized
199SQL labels in SELECT statements, but more crucially when generating constraint
200names from a naming convention. It is this area that has created the need for
201SQLAlchemy to change this default conservatively. For example, the following
202naming convention produces two very different constraint names based on the
203identifier length::
204
205 from sqlalchemy import Column
206 from sqlalchemy import Index
207 from sqlalchemy import Integer
208 from sqlalchemy import MetaData
209 from sqlalchemy import Table
210 from sqlalchemy.dialects import oracle
211 from sqlalchemy.schema import CreateIndex
212
213 m = MetaData(naming_convention={"ix": "ix_%(column_0N_name)s"})
214
215 t = Table(
216 "t",
217 m,
218 Column("some_column_name_1", Integer),
219 Column("some_column_name_2", Integer),
220 Column("some_column_name_3", Integer),
221 )
222
223 ix = Index(
224 None,
225 t.c.some_column_name_1,
226 t.c.some_column_name_2,
227 t.c.some_column_name_3,
228 )
229
230 oracle_dialect = oracle.dialect(max_identifier_length=30)
231 print(CreateIndex(ix).compile(dialect=oracle_dialect))
232
233With an identifier length of 30, the above CREATE INDEX looks like::
234
235 CREATE INDEX ix_some_column_name_1s_70cd ON t
236 (some_column_name_1, some_column_name_2, some_column_name_3)
237
238However with length=128, it becomes::
239
240 CREATE INDEX ix_some_column_name_1some_column_name_2some_column_name_3 ON t
241 (some_column_name_1, some_column_name_2, some_column_name_3)
242
243Applications which have run versions of SQLAlchemy prior to 1.4 on an Oracle
244server version 12.2 or greater are therefore subject to the scenario of a
245database migration that wishes to "DROP CONSTRAINT" on a name that was
246previously generated with the shorter length. This migration will fail when
247the identifier length is changed without the name of the index or constraint
248first being adjusted. Such applications are strongly advised to make use of
249:paramref:`_sa.create_engine.max_identifier_length`
250in order to maintain control
251of the generation of truncated names, and to fully review and test all database
252migrations in a staging environment when changing this value to ensure that the
253impact of this change has been mitigated.
254
255.. versionchanged:: 1.4 the default max_identifier_length for Oracle is 128
256 characters, which is adjusted down to 30 upon first connect if an older
257 version of Oracle server (compatibility version < 12.2) is detected.
258
259
260LIMIT/OFFSET/FETCH Support
261--------------------------
262
263Methods like :meth:`_sql.Select.limit` and :meth:`_sql.Select.offset` make
264use of ``FETCH FIRST N ROW / OFFSET N ROWS`` syntax assuming
265Oracle 12c or above, and assuming the SELECT statement is not embedded within
266a compound statement like UNION. This syntax is also available directly by using
267the :meth:`_sql.Select.fetch` method.
268
269.. versionchanged:: 2.0 the Oracle dialect now uses
270 ``FETCH FIRST N ROW / OFFSET N ROWS`` for all
271 :meth:`_sql.Select.limit` and :meth:`_sql.Select.offset` usage including
272 within the ORM and legacy :class:`_orm.Query`. To force the legacy
273 behavior using window functions, specify the ``enable_offset_fetch=False``
274 dialect parameter to :func:`_sa.create_engine`.
275
276The use of ``FETCH FIRST / OFFSET`` may be disabled on any Oracle version
277by passing ``enable_offset_fetch=False`` to :func:`_sa.create_engine`, which
278will force the use of "legacy" mode that makes use of window functions.
279This mode is also selected automatically when using a version of Oracle
280prior to 12c.
281
282When using legacy mode, or when a :class:`.Select` statement
283with limit/offset is embedded in a compound statement, an emulated approach for
284LIMIT / OFFSET based on window functions is used, which involves creation of a
285subquery using ``ROW_NUMBER`` that is prone to performance issues as well as
286SQL construction issues for complex statements. However, this approach is
287supported by all Oracle versions. See notes below.
288
289Notes on LIMIT / OFFSET emulation (when fetch() method cannot be used)
290~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
291
292If using :meth:`_sql.Select.limit` and :meth:`_sql.Select.offset`, or with the
293ORM the :meth:`_orm.Query.limit` and :meth:`_orm.Query.offset` methods on an
294Oracle version prior to 12c, the following notes apply:
295
296* SQLAlchemy currently makes use of ROWNUM to achieve
297 LIMIT/OFFSET; the exact methodology is taken from
298 https://blogs.oracle.com/oraclemagazine/on-rownum-and-limiting-results .
299
300* the "FIRST_ROWS()" optimization keyword is not used by default. To enable
301 the usage of this optimization directive, specify ``optimize_limits=True``
302 to :func:`_sa.create_engine`.
303
304 .. versionchanged:: 1.4
305 The Oracle dialect renders limit/offset integer values using a "post
306 compile" scheme which renders the integer directly before passing the
307 statement to the cursor for execution. The ``use_binds_for_limits`` flag
308 no longer has an effect.
309
310 .. seealso::
311
312 :ref:`change_4808`.
313
314.. _oracle_returning:
315
316RETURNING Support
317-----------------
318
319The Oracle database supports RETURNING fully for INSERT, UPDATE and DELETE
320statements that are invoked with a single collection of bound parameters
321(that is, a ``cursor.execute()`` style statement; SQLAlchemy does not generally
322support RETURNING with :term:`executemany` statements). Multiple rows may be
323returned as well.
324
325.. versionchanged:: 2.0 the Oracle backend has full support for RETURNING
326 on parity with other backends.
327
328
329
330ON UPDATE CASCADE
331-----------------
332
333Oracle doesn't have native ON UPDATE CASCADE functionality. A trigger based
334solution is available at
335https://asktom.oracle.com/tkyte/update_cascade/index.html .
336
337When using the SQLAlchemy ORM, the ORM has limited ability to manually issue
338cascading updates - specify ForeignKey objects using the
339"deferrable=True, initially='deferred'" keyword arguments,
340and specify "passive_updates=False" on each relationship().
341
342Oracle 8 Compatibility
343----------------------
344
345.. warning:: The status of Oracle 8 compatibility is not known for SQLAlchemy
346 2.0.
347
348When Oracle 8 is detected, the dialect internally configures itself to the
349following behaviors:
350
351* the use_ansi flag is set to False. This has the effect of converting all
352 JOIN phrases into the WHERE clause, and in the case of LEFT OUTER JOIN
353 makes use of Oracle's (+) operator.
354
355* the NVARCHAR2 and NCLOB datatypes are no longer generated as DDL when
356 the :class:`~sqlalchemy.types.Unicode` is used - VARCHAR2 and CLOB are issued
357 instead. This because these types don't seem to work correctly on Oracle 8
358 even though they are available. The :class:`~sqlalchemy.types.NVARCHAR` and
359 :class:`~sqlalchemy.dialects.oracle.NCLOB` types will always generate
360 NVARCHAR2 and NCLOB.
361
362
363Synonym/DBLINK Reflection
364-------------------------
365
366When using reflection with Table objects, the dialect can optionally search
367for tables indicated by synonyms, either in local or remote schemas or
368accessed over DBLINK, by passing the flag ``oracle_resolve_synonyms=True`` as
369a keyword argument to the :class:`_schema.Table` construct::
370
371 some_table = Table('some_table', autoload_with=some_engine,
372 oracle_resolve_synonyms=True)
373
374When this flag is set, the given name (such as ``some_table`` above) will
375be searched not just in the ``ALL_TABLES`` view, but also within the
376``ALL_SYNONYMS`` view to see if this name is actually a synonym to another
377name. If the synonym is located and refers to a DBLINK, the oracle dialect
378knows how to locate the table's information using DBLINK syntax(e.g.
379``@dblink``).
380
381``oracle_resolve_synonyms`` is accepted wherever reflection arguments are
382accepted, including methods such as :meth:`_schema.MetaData.reflect` and
383:meth:`_reflection.Inspector.get_columns`.
384
385If synonyms are not in use, this flag should be left disabled.
386
387.. _oracle_constraint_reflection:
388
389Constraint Reflection
390---------------------
391
392The Oracle dialect can return information about foreign key, unique, and
393CHECK constraints, as well as indexes on tables.
394
395Raw information regarding these constraints can be acquired using
396:meth:`_reflection.Inspector.get_foreign_keys`,
397:meth:`_reflection.Inspector.get_unique_constraints`,
398:meth:`_reflection.Inspector.get_check_constraints`, and
399:meth:`_reflection.Inspector.get_indexes`.
400
401.. versionchanged:: 1.2 The Oracle dialect can now reflect UNIQUE and
402 CHECK constraints.
403
404When using reflection at the :class:`_schema.Table` level, the
405:class:`_schema.Table`
406will also include these constraints.
407
408Note the following caveats:
409
410* When using the :meth:`_reflection.Inspector.get_check_constraints` method,
411 Oracle
412 builds a special "IS NOT NULL" constraint for columns that specify
413 "NOT NULL". This constraint is **not** returned by default; to include
414 the "IS NOT NULL" constraints, pass the flag ``include_all=True``::
415
416 from sqlalchemy import create_engine, inspect
417
418 engine = create_engine("oracle+cx_oracle://s:t@dsn")
419 inspector = inspect(engine)
420 all_check_constraints = inspector.get_check_constraints(
421 "some_table", include_all=True)
422
423* in most cases, when reflecting a :class:`_schema.Table`,
424 a UNIQUE constraint will
425 **not** be available as a :class:`.UniqueConstraint` object, as Oracle
426 mirrors unique constraints with a UNIQUE index in most cases (the exception
427 seems to be when two or more unique constraints represent the same columns);
428 the :class:`_schema.Table` will instead represent these using
429 :class:`.Index`
430 with the ``unique=True`` flag set.
431
432* Oracle creates an implicit index for the primary key of a table; this index
433 is **excluded** from all index results.
434
435* the list of columns reflected for an index will not include column names
436 that start with SYS_NC.
437
438Table names with SYSTEM/SYSAUX tablespaces
439-------------------------------------------
440
441The :meth:`_reflection.Inspector.get_table_names` and
442:meth:`_reflection.Inspector.get_temp_table_names`
443methods each return a list of table names for the current engine. These methods
444are also part of the reflection which occurs within an operation such as
445:meth:`_schema.MetaData.reflect`. By default,
446these operations exclude the ``SYSTEM``
447and ``SYSAUX`` tablespaces from the operation. In order to change this, the
448default list of tablespaces excluded can be changed at the engine level using
449the ``exclude_tablespaces`` parameter::
450
451 # exclude SYSAUX and SOME_TABLESPACE, but not SYSTEM
452 e = create_engine(
453 "oracle+cx_oracle://scott:tiger@xe",
454 exclude_tablespaces=["SYSAUX", "SOME_TABLESPACE"])
455
456DateTime Compatibility
457----------------------
458
459Oracle has no datatype known as ``DATETIME``, it instead has only ``DATE``,
460which can actually store a date and time value. For this reason, the Oracle
461dialect provides a type :class:`_oracle.DATE` which is a subclass of
462:class:`.DateTime`. This type has no special behavior, and is only
463present as a "marker" for this type; additionally, when a database column
464is reflected and the type is reported as ``DATE``, the time-supporting
465:class:`_oracle.DATE` type is used.
466
467.. _oracle_table_options:
468
469Oracle Table Options
470-------------------------
471
472The CREATE TABLE phrase supports the following options with Oracle
473in conjunction with the :class:`_schema.Table` construct:
474
475
476* ``ON COMMIT``::
477
478 Table(
479 "some_table", metadata, ...,
480 prefixes=['GLOBAL TEMPORARY'], oracle_on_commit='PRESERVE ROWS')
481
482* ``COMPRESS``::
483
484 Table('mytable', metadata, Column('data', String(32)),
485 oracle_compress=True)
486
487 Table('mytable', metadata, Column('data', String(32)),
488 oracle_compress=6)
489
490 The ``oracle_compress`` parameter accepts either an integer compression
491 level, or ``True`` to use the default compression level.
492
493.. _oracle_index_options:
494
495Oracle Specific Index Options
496-----------------------------
497
498Bitmap Indexes
499~~~~~~~~~~~~~~
500
501You can specify the ``oracle_bitmap`` parameter to create a bitmap index
502instead of a B-tree index::
503
504 Index('my_index', my_table.c.data, oracle_bitmap=True)
505
506Bitmap indexes cannot be unique and cannot be compressed. SQLAlchemy will not
507check for such limitations, only the database will.
508
509Index compression
510~~~~~~~~~~~~~~~~~
511
512Oracle has a more efficient storage mode for indexes containing lots of
513repeated values. Use the ``oracle_compress`` parameter to turn on key
514compression::
515
516 Index('my_index', my_table.c.data, oracle_compress=True)
517
518 Index('my_index', my_table.c.data1, my_table.c.data2, unique=True,
519 oracle_compress=1)
520
521The ``oracle_compress`` parameter accepts either an integer specifying the
522number of prefix columns to compress, or ``True`` to use the default (all
523columns for non-unique indexes, all but the last column for unique indexes).
524
525""" # noqa
526
527from __future__ import annotations
528
529from collections import defaultdict
530from functools import lru_cache
531from functools import wraps
532import re
533
534from . import dictionary
535from .types import _OracleBoolean
536from .types import _OracleDate
537from .types import BFILE
538from .types import BINARY_DOUBLE
539from .types import BINARY_FLOAT
540from .types import DATE
541from .types import FLOAT
542from .types import INTERVAL
543from .types import LONG
544from .types import NCLOB
545from .types import NUMBER
546from .types import NVARCHAR2 # noqa
547from .types import OracleRaw # noqa
548from .types import RAW
549from .types import ROWID # noqa
550from .types import TIMESTAMP
551from .types import VARCHAR2 # noqa
552from ... import Computed
553from ... import exc
554from ... import schema as sa_schema
555from ... import sql
556from ... import util
557from ...engine import default
558from ...engine import ObjectKind
559from ...engine import ObjectScope
560from ...engine import reflection
561from ...engine.reflection import ReflectionDefaults
562from ...sql import and_
563from ...sql import bindparam
564from ...sql import compiler
565from ...sql import expression
566from ...sql import func
567from ...sql import null
568from ...sql import or_
569from ...sql import select
570from ...sql import sqltypes
571from ...sql import util as sql_util
572from ...sql import visitors
573from ...sql.visitors import InternalTraversal
574from ...types import BLOB
575from ...types import CHAR
576from ...types import CLOB
577from ...types import DOUBLE_PRECISION
578from ...types import INTEGER
579from ...types import NCHAR
580from ...types import NVARCHAR
581from ...types import REAL
582from ...types import VARCHAR
583
584RESERVED_WORDS = set(
585 "SHARE RAW DROP BETWEEN FROM DESC OPTION PRIOR LONG THEN "
586 "DEFAULT ALTER IS INTO MINUS INTEGER NUMBER GRANT IDENTIFIED "
587 "ALL TO ORDER ON FLOAT DATE HAVING CLUSTER NOWAIT RESOURCE "
588 "ANY TABLE INDEX FOR UPDATE WHERE CHECK SMALLINT WITH DELETE "
589 "BY ASC REVOKE LIKE SIZE RENAME NOCOMPRESS NULL GROUP VALUES "
590 "AS IN VIEW EXCLUSIVE COMPRESS SYNONYM SELECT INSERT EXISTS "
591 "NOT TRIGGER ELSE CREATE INTERSECT PCTFREE DISTINCT USER "
592 "CONNECT SET MODE OF UNIQUE VARCHAR2 VARCHAR LOCK OR CHAR "
593 "DECIMAL UNION PUBLIC AND START UID COMMENT CURRENT LEVEL".split()
594)
595
596NO_ARG_FNS = set(
597 "UID CURRENT_DATE SYSDATE USER CURRENT_TIME CURRENT_TIMESTAMP".split()
598)
599
600
601colspecs = {
602 sqltypes.Boolean: _OracleBoolean,
603 sqltypes.Interval: INTERVAL,
604 sqltypes.DateTime: DATE,
605 sqltypes.Date: _OracleDate,
606}
607
608ischema_names = {
609 "VARCHAR2": VARCHAR,
610 "NVARCHAR2": NVARCHAR,
611 "CHAR": CHAR,
612 "NCHAR": NCHAR,
613 "DATE": DATE,
614 "NUMBER": NUMBER,
615 "BLOB": BLOB,
616 "BFILE": BFILE,
617 "CLOB": CLOB,
618 "NCLOB": NCLOB,
619 "TIMESTAMP": TIMESTAMP,
620 "TIMESTAMP WITH TIME ZONE": TIMESTAMP,
621 "TIMESTAMP WITH LOCAL TIME ZONE": TIMESTAMP,
622 "INTERVAL DAY TO SECOND": INTERVAL,
623 "RAW": RAW,
624 "FLOAT": FLOAT,
625 "DOUBLE PRECISION": DOUBLE_PRECISION,
626 "REAL": REAL,
627 "LONG": LONG,
628 "BINARY_DOUBLE": BINARY_DOUBLE,
629 "BINARY_FLOAT": BINARY_FLOAT,
630 "ROWID": ROWID,
631}
632
633
634class OracleTypeCompiler(compiler.GenericTypeCompiler):
635 # Note:
636 # Oracle DATE == DATETIME
637 # Oracle does not allow milliseconds in DATE
638 # Oracle does not support TIME columns
639
640 def visit_datetime(self, type_, **kw):
641 return self.visit_DATE(type_, **kw)
642
643 def visit_float(self, type_, **kw):
644 return self.visit_FLOAT(type_, **kw)
645
646 def visit_double(self, type_, **kw):
647 return self.visit_DOUBLE_PRECISION(type_, **kw)
648
649 def visit_unicode(self, type_, **kw):
650 if self.dialect._use_nchar_for_unicode:
651 return self.visit_NVARCHAR2(type_, **kw)
652 else:
653 return self.visit_VARCHAR2(type_, **kw)
654
655 def visit_INTERVAL(self, type_, **kw):
656 return "INTERVAL DAY%s TO SECOND%s" % (
657 type_.day_precision is not None
658 and "(%d)" % type_.day_precision
659 or "",
660 type_.second_precision is not None
661 and "(%d)" % type_.second_precision
662 or "",
663 )
664
665 def visit_LONG(self, type_, **kw):
666 return "LONG"
667
668 def visit_TIMESTAMP(self, type_, **kw):
669 if getattr(type_, "local_timezone", False):
670 return "TIMESTAMP WITH LOCAL TIME ZONE"
671 elif type_.timezone:
672 return "TIMESTAMP WITH TIME ZONE"
673 else:
674 return "TIMESTAMP"
675
676 def visit_DOUBLE_PRECISION(self, type_, **kw):
677 return self._generate_numeric(type_, "DOUBLE PRECISION", **kw)
678
679 def visit_BINARY_DOUBLE(self, type_, **kw):
680 return self._generate_numeric(type_, "BINARY_DOUBLE", **kw)
681
682 def visit_BINARY_FLOAT(self, type_, **kw):
683 return self._generate_numeric(type_, "BINARY_FLOAT", **kw)
684
685 def visit_FLOAT(self, type_, **kw):
686 kw["_requires_binary_precision"] = True
687 return self._generate_numeric(type_, "FLOAT", **kw)
688
689 def visit_NUMBER(self, type_, **kw):
690 return self._generate_numeric(type_, "NUMBER", **kw)
691
692 def _generate_numeric(
693 self,
694 type_,
695 name,
696 precision=None,
697 scale=None,
698 _requires_binary_precision=False,
699 **kw,
700 ):
701 if precision is None:
702 precision = getattr(type_, "precision", None)
703
704 if _requires_binary_precision:
705 binary_precision = getattr(type_, "binary_precision", None)
706
707 if precision and binary_precision is None:
708 # https://www.oracletutorial.com/oracle-basics/oracle-float/
709 estimated_binary_precision = int(precision / 0.30103)
710 raise exc.ArgumentError(
711 "Oracle FLOAT types use 'binary precision', which does "
712 "not convert cleanly from decimal 'precision'. Please "
713 "specify "
714 f"this type with a separate Oracle variant, such as "
715 f"{type_.__class__.__name__}(precision={precision})."
716 f"with_variant(oracle.FLOAT"
717 f"(binary_precision="
718 f"{estimated_binary_precision}), 'oracle'), so that the "
719 "Oracle specific 'binary_precision' may be specified "
720 "accurately."
721 )
722 else:
723 precision = binary_precision
724
725 if scale is None:
726 scale = getattr(type_, "scale", None)
727
728 if precision is None:
729 return name
730 elif scale is None:
731 n = "%(name)s(%(precision)s)"
732 return n % {"name": name, "precision": precision}
733 else:
734 n = "%(name)s(%(precision)s, %(scale)s)"
735 return n % {"name": name, "precision": precision, "scale": scale}
736
737 def visit_string(self, type_, **kw):
738 return self.visit_VARCHAR2(type_, **kw)
739
740 def visit_VARCHAR2(self, type_, **kw):
741 return self._visit_varchar(type_, "", "2")
742
743 def visit_NVARCHAR2(self, type_, **kw):
744 return self._visit_varchar(type_, "N", "2")
745
746 visit_NVARCHAR = visit_NVARCHAR2
747
748 def visit_VARCHAR(self, type_, **kw):
749 return self._visit_varchar(type_, "", "")
750
751 def _visit_varchar(self, type_, n, num):
752 if not type_.length:
753 return "%(n)sVARCHAR%(two)s" % {"two": num, "n": n}
754 elif not n and self.dialect._supports_char_length:
755 varchar = "VARCHAR%(two)s(%(length)s CHAR)"
756 return varchar % {"length": type_.length, "two": num}
757 else:
758 varchar = "%(n)sVARCHAR%(two)s(%(length)s)"
759 return varchar % {"length": type_.length, "two": num, "n": n}
760
761 def visit_text(self, type_, **kw):
762 return self.visit_CLOB(type_, **kw)
763
764 def visit_unicode_text(self, type_, **kw):
765 if self.dialect._use_nchar_for_unicode:
766 return self.visit_NCLOB(type_, **kw)
767 else:
768 return self.visit_CLOB(type_, **kw)
769
770 def visit_large_binary(self, type_, **kw):
771 return self.visit_BLOB(type_, **kw)
772
773 def visit_big_integer(self, type_, **kw):
774 return self.visit_NUMBER(type_, precision=19, **kw)
775
776 def visit_boolean(self, type_, **kw):
777 return self.visit_SMALLINT(type_, **kw)
778
779 def visit_RAW(self, type_, **kw):
780 if type_.length:
781 return "RAW(%(length)s)" % {"length": type_.length}
782 else:
783 return "RAW"
784
785 def visit_ROWID(self, type_, **kw):
786 return "ROWID"
787
788
789class OracleCompiler(compiler.SQLCompiler):
790 """Oracle compiler modifies the lexical structure of Select
791 statements to work under non-ANSI configured Oracle databases, if
792 the use_ansi flag is False.
793 """
794
795 compound_keywords = util.update_copy(
796 compiler.SQLCompiler.compound_keywords,
797 {expression.CompoundSelect.EXCEPT: "MINUS"},
798 )
799
800 def __init__(self, *args, **kwargs):
801 self.__wheres = {}
802 super().__init__(*args, **kwargs)
803
804 def visit_mod_binary(self, binary, operator, **kw):
805 return "mod(%s, %s)" % (
806 self.process(binary.left, **kw),
807 self.process(binary.right, **kw),
808 )
809
810 def visit_now_func(self, fn, **kw):
811 return "CURRENT_TIMESTAMP"
812
813 def visit_char_length_func(self, fn, **kw):
814 return "LENGTH" + self.function_argspec(fn, **kw)
815
816 def visit_match_op_binary(self, binary, operator, **kw):
817 return "CONTAINS (%s, %s)" % (
818 self.process(binary.left),
819 self.process(binary.right),
820 )
821
822 def visit_true(self, expr, **kw):
823 return "1"
824
825 def visit_false(self, expr, **kw):
826 return "0"
827
828 def get_cte_preamble(self, recursive):
829 return "WITH"
830
831 def get_select_hint_text(self, byfroms):
832 return " ".join("/*+ %s */" % text for table, text in byfroms.items())
833
834 def function_argspec(self, fn, **kw):
835 if len(fn.clauses) > 0 or fn.name.upper() not in NO_ARG_FNS:
836 return compiler.SQLCompiler.function_argspec(self, fn, **kw)
837 else:
838 return ""
839
840 def visit_function(self, func, **kw):
841 text = super().visit_function(func, **kw)
842 if kw.get("asfrom", False):
843 text = "TABLE (%s)" % text
844 return text
845
846 def visit_table_valued_column(self, element, **kw):
847 text = super().visit_table_valued_column(element, **kw)
848 text = text + ".COLUMN_VALUE"
849 return text
850
851 def default_from(self):
852 """Called when a ``SELECT`` statement has no froms,
853 and no ``FROM`` clause is to be appended.
854
855 The Oracle compiler tacks a "FROM DUAL" to the statement.
856 """
857
858 return " FROM DUAL"
859
860 def visit_join(self, join, from_linter=None, **kwargs):
861 if self.dialect.use_ansi:
862 return compiler.SQLCompiler.visit_join(
863 self, join, from_linter=from_linter, **kwargs
864 )
865 else:
866 if from_linter:
867 from_linter.edges.add((join.left, join.right))
868
869 kwargs["asfrom"] = True
870 if isinstance(join.right, expression.FromGrouping):
871 right = join.right.element
872 else:
873 right = join.right
874 return (
875 self.process(join.left, from_linter=from_linter, **kwargs)
876 + ", "
877 + self.process(right, from_linter=from_linter, **kwargs)
878 )
879
880 def _get_nonansi_join_whereclause(self, froms):
881 clauses = []
882
883 def visit_join(join):
884 if join.isouter:
885 # https://docs.oracle.com/database/121/SQLRF/queries006.htm#SQLRF52354
886 # "apply the outer join operator (+) to all columns of B in
887 # the join condition in the WHERE clause" - that is,
888 # unconditionally regardless of operator or the other side
889 def visit_binary(binary):
890 if isinstance(
891 binary.left, expression.ColumnClause
892 ) and join.right.is_derived_from(binary.left.table):
893 binary.left = _OuterJoinColumn(binary.left)
894 elif isinstance(
895 binary.right, expression.ColumnClause
896 ) and join.right.is_derived_from(binary.right.table):
897 binary.right = _OuterJoinColumn(binary.right)
898
899 clauses.append(
900 visitors.cloned_traverse(
901 join.onclause, {}, {"binary": visit_binary}
902 )
903 )
904 else:
905 clauses.append(join.onclause)
906
907 for j in join.left, join.right:
908 if isinstance(j, expression.Join):
909 visit_join(j)
910 elif isinstance(j, expression.FromGrouping):
911 visit_join(j.element)
912
913 for f in froms:
914 if isinstance(f, expression.Join):
915 visit_join(f)
916
917 if not clauses:
918 return None
919 else:
920 return sql.and_(*clauses)
921
922 def visit_outer_join_column(self, vc, **kw):
923 return self.process(vc.column, **kw) + "(+)"
924
925 def visit_sequence(self, seq, **kw):
926 return self.preparer.format_sequence(seq) + ".nextval"
927
928 def get_render_as_alias_suffix(self, alias_name_text):
929 """Oracle doesn't like ``FROM table AS alias``"""
930
931 return " " + alias_name_text
932
933 def returning_clause(
934 self, stmt, returning_cols, *, populate_result_map, **kw
935 ):
936 columns = []
937 binds = []
938
939 for i, column in enumerate(
940 expression._select_iterables(returning_cols)
941 ):
942 if (
943 self.isupdate
944 and isinstance(column, sa_schema.Column)
945 and isinstance(column.server_default, Computed)
946 and not self.dialect._supports_update_returning_computed_cols
947 ):
948 util.warn(
949 "Computed columns don't work with Oracle UPDATE "
950 "statements that use RETURNING; the value of the column "
951 "*before* the UPDATE takes place is returned. It is "
952 "advised to not use RETURNING with an Oracle computed "
953 "column. Consider setting implicit_returning to False on "
954 "the Table object in order to avoid implicit RETURNING "
955 "clauses from being generated for this Table."
956 )
957 if column.type._has_column_expression:
958 col_expr = column.type.column_expression(column)
959 else:
960 col_expr = column
961
962 outparam = sql.outparam("ret_%d" % i, type_=column.type)
963 self.binds[outparam.key] = outparam
964 binds.append(
965 self.bindparam_string(self._truncate_bindparam(outparam))
966 )
967
968 # has_out_parameters would in a normal case be set to True
969 # as a result of the compiler visiting an outparam() object.
970 # in this case, the above outparam() objects are not being
971 # visited. Ensure the statement itself didn't have other
972 # outparam() objects independently.
973 # technically, this could be supported, but as it would be
974 # a very strange use case without a clear rationale, disallow it
975 if self.has_out_parameters:
976 raise exc.InvalidRequestError(
977 "Using explicit outparam() objects with "
978 "UpdateBase.returning() in the same Core DML statement "
979 "is not supported in the Oracle dialect."
980 )
981
982 self._oracle_returning = True
983
984 columns.append(self.process(col_expr, within_columns_clause=False))
985 if populate_result_map:
986 self._add_to_result_map(
987 getattr(col_expr, "name", col_expr._anon_name_label),
988 getattr(col_expr, "name", col_expr._anon_name_label),
989 (
990 column,
991 getattr(column, "name", None),
992 getattr(column, "key", None),
993 ),
994 column.type,
995 )
996
997 return "RETURNING " + ", ".join(columns) + " INTO " + ", ".join(binds)
998
999 def _row_limit_clause(self, select, **kw):
1000 """ORacle 12c supports OFFSET/FETCH operators
1001 Use it instead subquery with row_number
1002
1003 """
1004
1005 if (
1006 select._fetch_clause is not None
1007 or not self.dialect._supports_offset_fetch
1008 ):
1009 return super()._row_limit_clause(
1010 select, use_literal_execute_for_simple_int=True, **kw
1011 )
1012 else:
1013 return self.fetch_clause(
1014 select,
1015 fetch_clause=self._get_limit_or_fetch(select),
1016 use_literal_execute_for_simple_int=True,
1017 **kw,
1018 )
1019
1020 def _get_limit_or_fetch(self, select):
1021 if select._fetch_clause is None:
1022 return select._limit_clause
1023 else:
1024 return select._fetch_clause
1025
1026 def translate_select_structure(self, select_stmt, **kwargs):
1027 select = select_stmt
1028
1029 if not getattr(select, "_oracle_visit", None):
1030 if not self.dialect.use_ansi:
1031 froms = self._display_froms_for_select(
1032 select, kwargs.get("asfrom", False)
1033 )
1034 whereclause = self._get_nonansi_join_whereclause(froms)
1035 if whereclause is not None:
1036 select = select.where(whereclause)
1037 select._oracle_visit = True
1038
1039 # if fetch is used this is not needed
1040 if (
1041 select._has_row_limiting_clause
1042 and not self.dialect._supports_offset_fetch
1043 and select._fetch_clause is None
1044 ):
1045 limit_clause = select._limit_clause
1046 offset_clause = select._offset_clause
1047
1048 if select._simple_int_clause(limit_clause):
1049 limit_clause = limit_clause.render_literal_execute()
1050
1051 if select._simple_int_clause(offset_clause):
1052 offset_clause = offset_clause.render_literal_execute()
1053
1054 # currently using form at:
1055 # https://blogs.oracle.com/oraclemagazine/\
1056 # on-rownum-and-limiting-results
1057
1058 orig_select = select
1059 select = select._generate()
1060 select._oracle_visit = True
1061
1062 # add expressions to accommodate FOR UPDATE OF
1063 for_update = select._for_update_arg
1064 if for_update is not None and for_update.of:
1065 for_update = for_update._clone()
1066 for_update._copy_internals()
1067
1068 for elem in for_update.of:
1069 if not select.selected_columns.contains_column(elem):
1070 select = select.add_columns(elem)
1071
1072 # Wrap the middle select and add the hint
1073 inner_subquery = select.alias()
1074 limitselect = sql.select(
1075 *[
1076 c
1077 for c in inner_subquery.c
1078 if orig_select.selected_columns.corresponding_column(c)
1079 is not None
1080 ]
1081 )
1082
1083 if (
1084 limit_clause is not None
1085 and self.dialect.optimize_limits
1086 and select._simple_int_clause(limit_clause)
1087 ):
1088 limitselect = limitselect.prefix_with(
1089 expression.text(
1090 "/*+ FIRST_ROWS(%s) */"
1091 % self.process(limit_clause, **kwargs)
1092 )
1093 )
1094
1095 limitselect._oracle_visit = True
1096 limitselect._is_wrapper = True
1097
1098 # add expressions to accommodate FOR UPDATE OF
1099 if for_update is not None and for_update.of:
1100 adapter = sql_util.ClauseAdapter(inner_subquery)
1101 for_update.of = [
1102 adapter.traverse(elem) for elem in for_update.of
1103 ]
1104
1105 # If needed, add the limiting clause
1106 if limit_clause is not None:
1107 if select._simple_int_clause(limit_clause) and (
1108 offset_clause is None
1109 or select._simple_int_clause(offset_clause)
1110 ):
1111 max_row = limit_clause
1112
1113 if offset_clause is not None:
1114 max_row = max_row + offset_clause
1115
1116 else:
1117 max_row = limit_clause
1118
1119 if offset_clause is not None:
1120 max_row = max_row + offset_clause
1121 limitselect = limitselect.where(
1122 sql.literal_column("ROWNUM") <= max_row
1123 )
1124
1125 # If needed, add the ora_rn, and wrap again with offset.
1126 if offset_clause is None:
1127 limitselect._for_update_arg = for_update
1128 select = limitselect
1129 else:
1130 limitselect = limitselect.add_columns(
1131 sql.literal_column("ROWNUM").label("ora_rn")
1132 )
1133 limitselect._oracle_visit = True
1134 limitselect._is_wrapper = True
1135
1136 if for_update is not None and for_update.of:
1137 limitselect_cols = limitselect.selected_columns
1138 for elem in for_update.of:
1139 if (
1140 limitselect_cols.corresponding_column(elem)
1141 is None
1142 ):
1143 limitselect = limitselect.add_columns(elem)
1144
1145 limit_subquery = limitselect.alias()
1146 origselect_cols = orig_select.selected_columns
1147 offsetselect = sql.select(
1148 *[
1149 c
1150 for c in limit_subquery.c
1151 if origselect_cols.corresponding_column(c)
1152 is not None
1153 ]
1154 )
1155
1156 offsetselect._oracle_visit = True
1157 offsetselect._is_wrapper = True
1158
1159 if for_update is not None and for_update.of:
1160 adapter = sql_util.ClauseAdapter(limit_subquery)
1161 for_update.of = [
1162 adapter.traverse(elem) for elem in for_update.of
1163 ]
1164
1165 offsetselect = offsetselect.where(
1166 sql.literal_column("ora_rn") > offset_clause
1167 )
1168
1169 offsetselect._for_update_arg = for_update
1170 select = offsetselect
1171
1172 return select
1173
1174 def limit_clause(self, select, **kw):
1175 return ""
1176
1177 def visit_empty_set_expr(self, type_, **kw):
1178 return "SELECT 1 FROM DUAL WHERE 1!=1"
1179
1180 def for_update_clause(self, select, **kw):
1181 if self.is_subquery():
1182 return ""
1183
1184 tmp = " FOR UPDATE"
1185
1186 if select._for_update_arg.of:
1187 tmp += " OF " + ", ".join(
1188 self.process(elem, **kw) for elem in select._for_update_arg.of
1189 )
1190
1191 if select._for_update_arg.nowait:
1192 tmp += " NOWAIT"
1193 if select._for_update_arg.skip_locked:
1194 tmp += " SKIP LOCKED"
1195
1196 return tmp
1197
1198 def visit_is_distinct_from_binary(self, binary, operator, **kw):
1199 return "DECODE(%s, %s, 0, 1) = 1" % (
1200 self.process(binary.left),
