You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					2511 lines
				
				85 KiB
			
		
		
			
		
	
	
					2511 lines
				
				85 KiB
			| 
								 
											3 years ago
										 
									 | 
							
								# oracle/base.py
							 | 
						||
| 
								 | 
							
								# Copyright (C) 2005-2022 the SQLAlchemy authors and contributors
							 | 
						||
| 
								 | 
							
								# <see AUTHORS file>
							 | 
						||
| 
								 | 
							
								#
							 | 
						||
| 
								 | 
							
								# This module is part of SQLAlchemy and is released under
							 | 
						||
| 
								 | 
							
								# the MIT License: https://www.opensource.org/licenses/mit-license.php
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								r"""
							 | 
						||
| 
								 | 
							
								.. dialect:: oracle
							 | 
						||
| 
								 | 
							
								    :name: Oracle
							 | 
						||
| 
								 | 
							
								    :full_support: 11.2, 18c
							 | 
						||
| 
								 | 
							
								    :normal_support: 11+
							 | 
						||
| 
								 | 
							
								    :best_effort: 8+
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Auto Increment Behavior
							 | 
						||
| 
								 | 
							
								-----------------------
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								SQLAlchemy Table objects which include integer primary keys are usually
							 | 
						||
| 
								 | 
							
								assumed to have "autoincrementing" behavior, meaning they can generate their
							 | 
						||
| 
								 | 
							
								own primary key values upon INSERT. For use within Oracle, two options are
							 | 
						||
| 
								 | 
							
								available, which are the use of IDENTITY columns (Oracle 12 and above only)
							 | 
						||
| 
								 | 
							
								or the association of a SEQUENCE with the column.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Specifying GENERATED AS IDENTITY (Oracle 12 and above)
							 | 
						||
| 
								 | 
							
								~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Starting from version 12 Oracle can make use of identity columns using
							 | 
						||
| 
								 | 
							
								the :class:`_sql.Identity` to specify the autoincrementing behavior::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    t = Table('mytable', metadata,
							 | 
						||
| 
								 | 
							
								        Column('id', Integer, Identity(start=3), primary_key=True),
							 | 
						||
| 
								 | 
							
								        Column(...), ...
							 | 
						||
| 
								 | 
							
								    )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								The CREATE TABLE for the above :class:`_schema.Table` object would be:
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								.. sourcecode:: sql
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    CREATE TABLE mytable (
							 | 
						||
| 
								 | 
							
								        id INTEGER GENERATED BY DEFAULT AS IDENTITY (START WITH 3),
							 | 
						||
| 
								 | 
							
								        ...,
							 | 
						||
| 
								 | 
							
								        PRIMARY KEY (id)
							 | 
						||
| 
								 | 
							
								    )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								The :class:`_schema.Identity` object support many options to control the
							 | 
						||
| 
								 | 
							
								"autoincrementing" behavior of the column, like the starting value, the
							 | 
						||
| 
								 | 
							
								incrementing value, etc.
							 | 
						||
| 
								 | 
							
								In addition to the standard options, Oracle supports setting
							 | 
						||
| 
								 | 
							
								:paramref:`_schema.Identity.always` to ``None`` to use the default
							 | 
						||
| 
								 | 
							
								generated mode, rendering GENERATED AS IDENTITY in the DDL. It also supports
							 | 
						||
| 
								 | 
							
								setting :paramref:`_schema.Identity.on_null` to ``True`` to specify ON NULL
							 | 
						||
| 
								 | 
							
								in conjunction with a 'BY DEFAULT' identity column.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Using a SEQUENCE (all Oracle versions)
							 | 
						||
| 
								 | 
							
								~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Older version of Oracle had no "autoincrement"
							 | 
						||
| 
								 | 
							
								feature, SQLAlchemy relies upon sequences to produce these values.   With the
							 | 
						||
| 
								 | 
							
								older Oracle versions, *a sequence must always be explicitly specified to
							 | 
						||
| 
								 | 
							
								enable autoincrement*.  This is divergent with the majority of documentation
							 | 
						||
| 
								 | 
							
								examples which assume the usage of an autoincrement-capable database.   To
							 | 
						||
| 
								 | 
							
								specify sequences, use the sqlalchemy.schema.Sequence object which is passed
							 | 
						||
| 
								 | 
							
								to a Column construct::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  t = Table('mytable', metadata,
							 | 
						||
| 
								 | 
							
								        Column('id', Integer, Sequence('id_seq'), primary_key=True),
							 | 
						||
| 
								 | 
							
								        Column(...), ...
							 | 
						||
| 
								 | 
							
								  )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								This step is also required when using table reflection, i.e. autoload_with=engine::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  t = Table('mytable', metadata,
							 | 
						||
| 
								 | 
							
								        Column('id', Integer, Sequence('id_seq'), primary_key=True),
							 | 
						||
| 
								 | 
							
								        autoload_with=engine
							 | 
						||
| 
								 | 
							
								  )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								.. versionchanged::  1.4   Added :class:`_schema.Identity` construct
							 | 
						||
| 
								 | 
							
								   in a :class:`_schema.Column` to specify the option of an autoincrementing
							 | 
						||
| 
								 | 
							
								   column.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								.. _oracle_isolation_level:
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Transaction Isolation Level / Autocommit
							 | 
						||
| 
								 | 
							
								----------------------------------------
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								The Oracle database supports "READ COMMITTED" and "SERIALIZABLE" modes of
							 | 
						||
| 
								 | 
							
								isolation. The AUTOCOMMIT isolation level is also supported by the cx_Oracle
							 | 
						||
| 
								 | 
							
								dialect.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								To set using per-connection execution options::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    connection = engine.connect()
							 | 
						||
| 
								 | 
							
								    connection = connection.execution_options(
							 | 
						||
| 
								 | 
							
								        isolation_level="AUTOCOMMIT"
							 | 
						||
| 
								 | 
							
								    )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								For ``READ COMMITTED`` and ``SERIALIZABLE``, the Oracle dialect sets the
							 | 
						||
| 
								 | 
							
								level at the session level using ``ALTER SESSION``, which is reverted back
							 | 
						||
| 
								 | 
							
								to its default setting when the connection is returned to the connection
							 | 
						||
| 
								 | 
							
								pool.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Valid values for ``isolation_level`` include:
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								* ``READ COMMITTED``
							 | 
						||
| 
								 | 
							
								* ``AUTOCOMMIT``
							 | 
						||
| 
								 | 
							
								* ``SERIALIZABLE``
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								.. note:: The implementation for the
							 | 
						||
| 
								 | 
							
								   :meth:`_engine.Connection.get_isolation_level` method as implemented by the
							 | 
						||
| 
								 | 
							
								   Oracle dialect necessarily forces the start of a transaction using the
							 | 
						||
| 
								 | 
							
								   Oracle LOCAL_TRANSACTION_ID function; otherwise no level is normally
							 | 
						||
| 
								 | 
							
								   readable.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								   Additionally, the :meth:`_engine.Connection.get_isolation_level` method will
							 | 
						||
| 
								 | 
							
								   raise an exception if the ``v$transaction`` view is not available due to
							 | 
						||
| 
								 | 
							
								   permissions or other reasons, which is a common occurrence in Oracle
							 | 
						||
| 
								 | 
							
								   installations.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								   The cx_Oracle dialect attempts to call the
							 | 
						||
| 
								 | 
							
								   :meth:`_engine.Connection.get_isolation_level` method when the dialect makes
							 | 
						||
| 
								 | 
							
								   its first connection to the database in order to acquire the
							 | 
						||
| 
								 | 
							
								   "default"isolation level.  This default level is necessary so that the level
							 | 
						||
| 
								 | 
							
								   can be reset on a connection after it has been temporarily modified using
							 | 
						||
| 
								 | 
							
								   :meth:`_engine.Connection.execution_options` method.   In the common event
							 | 
						||
| 
								 | 
							
								   that the :meth:`_engine.Connection.get_isolation_level` method raises an
							 | 
						||
| 
								 | 
							
								   exception due to ``v$transaction`` not being readable as well as any other
							 | 
						||
| 
								 | 
							
								   database-related failure, the level is assumed to be "READ COMMITTED".  No
							 | 
						||
| 
								 | 
							
								   warning is emitted for this initial first-connect condition as it is
							 | 
						||
| 
								 | 
							
								   expected to be a common restriction on Oracle databases.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								.. versionadded:: 1.3.16 added support for AUTOCOMMIT to the cx_oracle dialect
							 | 
						||
| 
								 | 
							
								   as well as the notion of a default isolation level
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								.. versionadded:: 1.3.21 Added support for SERIALIZABLE as well as live
							 | 
						||
| 
								 | 
							
								   reading of the isolation level.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								.. versionchanged:: 1.3.22 In the event that the default isolation
							 | 
						||
| 
								 | 
							
								   level cannot be read due to permissions on the v$transaction view as
							 | 
						||
| 
								 | 
							
								   is common in Oracle installations, the default isolation level is hardcoded
							 | 
						||
| 
								 | 
							
								   to "READ COMMITTED" which was the behavior prior to 1.3.21.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								.. seealso::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    :ref:`dbapi_autocommit`
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Identifier Casing
							 | 
						||
| 
								 | 
							
								-----------------
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								In Oracle, the data dictionary represents all case insensitive identifier
							 | 
						||
| 
								 | 
							
								names using UPPERCASE text.   SQLAlchemy on the other hand considers an
							 | 
						||
| 
								 | 
							
								all-lower case identifier name to be case insensitive.   The Oracle dialect
							 | 
						||
| 
								 | 
							
								converts all case insensitive identifiers to and from those two formats during
							 | 
						||
| 
								 | 
							
								schema level communication, such as reflection of tables and indexes.   Using
							 | 
						||
| 
								 | 
							
								an UPPERCASE name on the SQLAlchemy side indicates a case sensitive
							 | 
						||
| 
								 | 
							
								identifier, and SQLAlchemy will quote the name - this will cause mismatches
							 | 
						||
| 
								 | 
							
								against data dictionary data received from Oracle, so unless identifier names
							 | 
						||
| 
								 | 
							
								have been truly created as case sensitive (i.e. using quoted names), all
							 | 
						||
| 
								 | 
							
								lowercase names should be used on the SQLAlchemy side.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								.. _oracle_max_identifier_lengths:
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Max Identifier Lengths
							 | 
						||
| 
								 | 
							
								----------------------
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Oracle has changed the default max identifier length as of Oracle Server
							 | 
						||
| 
								 | 
							
								version 12.2.   Prior to this version, the length was 30, and for 12.2 and
							 | 
						||
| 
								 | 
							
								greater it is now 128.   This change impacts SQLAlchemy in the area of
							 | 
						||
| 
								 | 
							
								generated SQL label names as well as the generation of constraint names,
							 | 
						||
| 
								 | 
							
								particularly in the case where the constraint naming convention feature
							 | 
						||
| 
								 | 
							
								described at :ref:`constraint_naming_conventions` is being used.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								To assist with this change and others, Oracle includes the concept of a
							 | 
						||
| 
								 | 
							
								"compatibility" version, which is a version number that is independent of the
							 | 
						||
| 
								 | 
							
								actual server version in order to assist with migration of Oracle databases,
							 | 
						||
| 
								 | 
							
								and may be configured within the Oracle server itself. This compatibility
							 | 
						||
| 
								 | 
							
								version is retrieved using the query  ``SELECT value FROM v$parameter WHERE
							 | 
						||
| 
								 | 
							
								name = 'compatible';``.   The SQLAlchemy Oracle dialect, when tasked with
							 | 
						||
| 
								 | 
							
								determining the default max identifier length, will attempt to use this query
							 | 
						||
| 
								 | 
							
								upon first connect in order to determine the effective compatibility version of
							 | 
						||
| 
								 | 
							
								the server, which determines what the maximum allowed identifier length is for
							 | 
						||
| 
								 | 
							
								the server.  If the table is not available, the  server version information is
							 | 
						||
| 
								 | 
							
								used instead.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								As of SQLAlchemy 1.4, the default max identifier length for the Oracle dialect
							 | 
						||
| 
								 | 
							
								is 128 characters.  Upon first connect, the compatibility version is detected
							 | 
						||
| 
								 | 
							
								and if it is less than Oracle version 12.2, the max identifier length is
							 | 
						||
| 
								 | 
							
								changed to be 30 characters.  In all cases, setting the
							 | 
						||
| 
								 | 
							
								:paramref:`_sa.create_engine.max_identifier_length` parameter will bypass this
							 | 
						||
| 
								 | 
							
								change and the value given will be used as is::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    engine = create_engine(
							 | 
						||
| 
								 | 
							
								        "oracle+cx_oracle://scott:tiger@oracle122",
							 | 
						||
| 
								 | 
							
								        max_identifier_length=30)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								The maximum identifier length comes into play both when generating anonymized
							 | 
						||
| 
								 | 
							
								SQL labels in SELECT statements, but more crucially when generating constraint
							 | 
						||
| 
								 | 
							
								names from a naming convention.  It is this area that has created the need for
							 | 
						||
| 
								 | 
							
								SQLAlchemy to change this default conservatively.   For example, the following
							 | 
						||
| 
								 | 
							
								naming convention produces two very different constraint names based on the
							 | 
						||
| 
								 | 
							
								identifier length::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    from sqlalchemy import Column
							 | 
						||
| 
								 | 
							
								    from sqlalchemy import Index
							 | 
						||
| 
								 | 
							
								    from sqlalchemy import Integer
							 | 
						||
| 
								 | 
							
								    from sqlalchemy import MetaData
							 | 
						||
| 
								 | 
							
								    from sqlalchemy import Table
							 | 
						||
| 
								 | 
							
								    from sqlalchemy.dialects import oracle
							 | 
						||
| 
								 | 
							
								    from sqlalchemy.schema import CreateIndex
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    m = MetaData(naming_convention={"ix": "ix_%(column_0N_name)s"})
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    t = Table(
							 | 
						||
| 
								 | 
							
								        "t",
							 | 
						||
| 
								 | 
							
								        m,
							 | 
						||
| 
								 | 
							
								        Column("some_column_name_1", Integer),
							 | 
						||
| 
								 | 
							
								        Column("some_column_name_2", Integer),
							 | 
						||
| 
								 | 
							
								        Column("some_column_name_3", Integer),
							 | 
						||
| 
								 | 
							
								    )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    ix = Index(
							 | 
						||
| 
								 | 
							
								        None,
							 | 
						||
| 
								 | 
							
								        t.c.some_column_name_1,
							 | 
						||
| 
								 | 
							
								        t.c.some_column_name_2,
							 | 
						||
| 
								 | 
							
								        t.c.some_column_name_3,
							 | 
						||
| 
								 | 
							
								    )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    oracle_dialect = oracle.dialect(max_identifier_length=30)
							 | 
						||
| 
								 | 
							
								    print(CreateIndex(ix).compile(dialect=oracle_dialect))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								With an identifier length of 30, the above CREATE INDEX looks like::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    CREATE INDEX ix_some_column_name_1s_70cd ON t
							 | 
						||
| 
								 | 
							
								    (some_column_name_1, some_column_name_2, some_column_name_3)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								However with length=128, it becomes::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    CREATE INDEX ix_some_column_name_1some_column_name_2some_column_name_3 ON t
							 | 
						||
| 
								 | 
							
								    (some_column_name_1, some_column_name_2, some_column_name_3)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Applications which have run versions of SQLAlchemy prior to 1.4 on an  Oracle
							 | 
						||
| 
								 | 
							
								server version 12.2 or greater are therefore subject to the scenario of a
							 | 
						||
| 
								 | 
							
								database migration that wishes to "DROP CONSTRAINT" on a name that was
							 | 
						||
| 
								 | 
							
								previously generated with the shorter length.  This migration will fail when
							 | 
						||
| 
								 | 
							
								the identifier length is changed without the name of the index or constraint
							 | 
						||
| 
								 | 
							
								first being adjusted.  Such applications are strongly advised to make use of
							 | 
						||
| 
								 | 
							
								:paramref:`_sa.create_engine.max_identifier_length`
							 | 
						||
| 
								 | 
							
								in order to maintain control
							 | 
						||
| 
								 | 
							
								of the generation of truncated names, and to fully review and test all database
							 | 
						||
| 
								 | 
							
								migrations in a staging environment when changing this value to ensure that the
							 | 
						||
| 
								 | 
							
								impact of this change has been mitigated.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								.. versionchanged:: 1.4 the default max_identifier_length for Oracle is 128
							 | 
						||
| 
								 | 
							
								   characters, which is adjusted down to 30 upon first connect if an older
							 | 
						||
| 
								 | 
							
								   version of Oracle server (compatibility version < 12.2) is detected.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								LIMIT/OFFSET Support
							 | 
						||
| 
								 | 
							
								--------------------
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Oracle has no direct support for LIMIT and OFFSET until version 12c.
							 | 
						||
| 
								 | 
							
								To achieve this behavior across all widely used versions of Oracle starting
							 | 
						||
| 
								 | 
							
								with the 8 series, SQLAlchemy currently makes use of ROWNUM to achieve
							 | 
						||
| 
								 | 
							
								LIMIT/OFFSET; the exact methodology is taken from
							 | 
						||
| 
								 | 
							
								https://blogs.oracle.com/oraclemagazine/on-rownum-and-limiting-results .
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								There is currently a single option to affect its behavior:
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								* the "FIRST_ROWS()" optimization keyword is not used by default.  To enable
							 | 
						||
| 
								 | 
							
								  the usage of this optimization directive, specify ``optimize_limits=True``
							 | 
						||
| 
								 | 
							
								  to :func:`_sa.create_engine`.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								.. versionchanged:: 1.4
							 | 
						||
| 
								 | 
							
								    The Oracle dialect renders limit/offset integer values using a "post
							 | 
						||
| 
								 | 
							
								    compile" scheme which renders the integer directly before passing the
							 | 
						||
| 
								 | 
							
								    statement to the cursor for execution.   The ``use_binds_for_limits`` flag
							 | 
						||
| 
								 | 
							
								    no longer has an effect.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    .. seealso::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :ref:`change_4808`.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Support for changing the row number strategy, which would include one that
							 | 
						||
| 
								 | 
							
								makes use of the ``row_number()`` window function as well as one that makes
							 | 
						||
| 
								 | 
							
								use of the Oracle 12c  "FETCH FIRST N ROW / OFFSET N ROWS" keywords may be
							 | 
						||
| 
								 | 
							
								added in a future release.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								.. _oracle_returning:
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								RETURNING Support
							 | 
						||
| 
								 | 
							
								-----------------
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								The Oracle database supports a limited form of RETURNING, in order to retrieve
							 | 
						||
| 
								 | 
							
								result sets of matched rows from INSERT, UPDATE and DELETE statements.
							 | 
						||
| 
								 | 
							
								Oracle's RETURNING..INTO syntax only supports one row being returned, as it
							 | 
						||
| 
								 | 
							
								relies upon OUT parameters in order to function.  In addition, supported
							 | 
						||
| 
								 | 
							
								DBAPIs have further limitations (see :ref:`cx_oracle_returning`).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								SQLAlchemy's "implicit returning" feature, which employs RETURNING within an
							 | 
						||
| 
								 | 
							
								INSERT and sometimes an UPDATE statement in order to fetch newly generated
							 | 
						||
| 
								 | 
							
								primary key values and other SQL defaults and expressions, is normally enabled
							 | 
						||
| 
								 | 
							
								on the Oracle backend.  By default, "implicit returning" typically only
							 | 
						||
| 
								 | 
							
								fetches the value of a single ``nextval(some_seq)`` expression embedded into
							 | 
						||
| 
								 | 
							
								an INSERT in order to increment a sequence within an INSERT statement and get
							 | 
						||
| 
								 | 
							
								the value back at the same time. To disable this feature across the board,
							 | 
						||
| 
								 | 
							
								specify ``implicit_returning=False`` to :func:`_sa.create_engine`::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    engine = create_engine("oracle://scott:tiger@dsn",
							 | 
						||
| 
								 | 
							
								                           implicit_returning=False)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Implicit returning can also be disabled on a table-by-table basis as a table
							 | 
						||
| 
								 | 
							
								option::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    # Core Table
							 | 
						||
| 
								 | 
							
								    my_table = Table("my_table", metadata, ..., implicit_returning=False)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    # declarative
							 | 
						||
| 
								 | 
							
								    class MyClass(Base):
							 | 
						||
| 
								 | 
							
								        __tablename__ = 'my_table'
							 | 
						||
| 
								 | 
							
								        __table_args__ = {"implicit_returning": False}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								.. seealso::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    :ref:`cx_oracle_returning` - additional cx_oracle-specific restrictions on
							 | 
						||
| 
								 | 
							
								    implicit returning.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								ON UPDATE CASCADE
							 | 
						||
| 
								 | 
							
								-----------------
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Oracle doesn't have native ON UPDATE CASCADE functionality.  A trigger based
							 | 
						||
| 
								 | 
							
								solution is available at
							 | 
						||
| 
								 | 
							
								https://asktom.oracle.com/tkyte/update_cascade/index.html .
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								When using the SQLAlchemy ORM, the ORM has limited ability to manually issue
							 | 
						||
| 
								 | 
							
								cascading updates - specify ForeignKey objects using the
							 | 
						||
| 
								 | 
							
								"deferrable=True, initially='deferred'" keyword arguments,
							 | 
						||
| 
								 | 
							
								and specify "passive_updates=False" on each relationship().
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Oracle 8 Compatibility
							 | 
						||
| 
								 | 
							
								----------------------
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								When Oracle 8 is detected, the dialect internally configures itself to the
							 | 
						||
| 
								 | 
							
								following behaviors:
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								* the use_ansi flag is set to False.  This has the effect of converting all
							 | 
						||
| 
								 | 
							
								  JOIN phrases into the WHERE clause, and in the case of LEFT OUTER JOIN
							 | 
						||
| 
								 | 
							
								  makes use of Oracle's (+) operator.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								* the NVARCHAR2 and NCLOB datatypes are no longer generated as DDL when
							 | 
						||
| 
								 | 
							
								  the :class:`~sqlalchemy.types.Unicode` is used - VARCHAR2 and CLOB are
							 | 
						||
| 
								 | 
							
								  issued instead.   This because these types don't seem to work correctly on
							 | 
						||
| 
								 | 
							
								  Oracle 8 even though they are available.  The
							 | 
						||
| 
								 | 
							
								  :class:`~sqlalchemy.types.NVARCHAR` and
							 | 
						||
| 
								 | 
							
								  :class:`~sqlalchemy.dialects.oracle.NCLOB` types will always generate
							 | 
						||
| 
								 | 
							
								  NVARCHAR2 and NCLOB.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								* the "native unicode" mode is disabled when using cx_oracle, i.e. SQLAlchemy
							 | 
						||
| 
								 | 
							
								  encodes all Python unicode objects to "string" before passing in as bind
							 | 
						||
| 
								 | 
							
								  parameters.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Synonym/DBLINK Reflection
							 | 
						||
| 
								 | 
							
								-------------------------
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								When using reflection with Table objects, the dialect can optionally search
							 | 
						||
| 
								 | 
							
								for tables indicated by synonyms, either in local or remote schemas or
							 | 
						||
| 
								 | 
							
								accessed over DBLINK, by passing the flag ``oracle_resolve_synonyms=True`` as
							 | 
						||
| 
								 | 
							
								a keyword argument to the :class:`_schema.Table` construct::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    some_table = Table('some_table', autoload_with=some_engine,
							 | 
						||
| 
								 | 
							
								                                oracle_resolve_synonyms=True)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								When this flag is set, the given name (such as ``some_table`` above) will
							 | 
						||
| 
								 | 
							
								be searched not just in the ``ALL_TABLES`` view, but also within the
							 | 
						||
| 
								 | 
							
								``ALL_SYNONYMS`` view to see if this name is actually a synonym to another
							 | 
						||
| 
								 | 
							
								name.  If the synonym is located and refers to a DBLINK, the oracle dialect
							 | 
						||
| 
								 | 
							
								knows how to locate the table's information using DBLINK syntax(e.g.
							 | 
						||
| 
								 | 
							
								``@dblink``).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								``oracle_resolve_synonyms`` is accepted wherever reflection arguments are
							 | 
						||
| 
								 | 
							
								accepted, including methods such as :meth:`_schema.MetaData.reflect` and
							 | 
						||
| 
								 | 
							
								:meth:`_reflection.Inspector.get_columns`.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								If synonyms are not in use, this flag should be left disabled.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								.. _oracle_constraint_reflection:
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Constraint Reflection
							 | 
						||
| 
								 | 
							
								---------------------
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								The Oracle dialect can return information about foreign key, unique, and
							 | 
						||
| 
								 | 
							
								CHECK constraints, as well as indexes on tables.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Raw information regarding these constraints can be acquired using
							 | 
						||
| 
								 | 
							
								:meth:`_reflection.Inspector.get_foreign_keys`,
							 | 
						||
| 
								 | 
							
								:meth:`_reflection.Inspector.get_unique_constraints`,
							 | 
						||
| 
								 | 
							
								:meth:`_reflection.Inspector.get_check_constraints`, and
							 | 
						||
| 
								 | 
							
								:meth:`_reflection.Inspector.get_indexes`.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								.. versionchanged:: 1.2  The Oracle dialect can now reflect UNIQUE and
							 | 
						||
| 
								 | 
							
								   CHECK constraints.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								When using reflection at the :class:`_schema.Table` level, the
							 | 
						||
| 
								 | 
							
								:class:`_schema.Table`
							 | 
						||
| 
								 | 
							
								will also include these constraints.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Note the following caveats:
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								* When using the :meth:`_reflection.Inspector.get_check_constraints` method,
							 | 
						||
| 
								 | 
							
								  Oracle
							 | 
						||
| 
								 | 
							
								  builds a special "IS NOT NULL" constraint for columns that specify
							 | 
						||
| 
								 | 
							
								  "NOT NULL".  This constraint is **not** returned by default; to include
							 | 
						||
| 
								 | 
							
								  the "IS NOT NULL" constraints, pass the flag ``include_all=True``::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      from sqlalchemy import create_engine, inspect
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      engine = create_engine("oracle+cx_oracle://s:t@dsn")
							 | 
						||
| 
								 | 
							
								      inspector = inspect(engine)
							 | 
						||
| 
								 | 
							
								      all_check_constraints = inspector.get_check_constraints(
							 | 
						||
| 
								 | 
							
								          "some_table", include_all=True)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								* in most cases, when reflecting a :class:`_schema.Table`,
							 | 
						||
| 
								 | 
							
								  a UNIQUE constraint will
							 | 
						||
| 
								 | 
							
								  **not** be available as a :class:`.UniqueConstraint` object, as Oracle
							 | 
						||
| 
								 | 
							
								  mirrors unique constraints with a UNIQUE index in most cases (the exception
							 | 
						||
| 
								 | 
							
								  seems to be when two or more unique constraints represent the same columns);
							 | 
						||
| 
								 | 
							
								  the :class:`_schema.Table` will instead represent these using
							 | 
						||
| 
								 | 
							
								  :class:`.Index`
							 | 
						||
| 
								 | 
							
								  with the ``unique=True`` flag set.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								* Oracle creates an implicit index for the primary key of a table; this index
							 | 
						||
| 
								 | 
							
								  is **excluded** from all index results.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								* the list of columns reflected for an index will not include column names
							 | 
						||
| 
								 | 
							
								  that start with SYS_NC.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Table names with SYSTEM/SYSAUX tablespaces
							 | 
						||
| 
								 | 
							
								-------------------------------------------
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								The :meth:`_reflection.Inspector.get_table_names` and
							 | 
						||
| 
								 | 
							
								:meth:`_reflection.Inspector.get_temp_table_names`
							 | 
						||
| 
								 | 
							
								methods each return a list of table names for the current engine. These methods
							 | 
						||
| 
								 | 
							
								are also part of the reflection which occurs within an operation such as
							 | 
						||
| 
								 | 
							
								:meth:`_schema.MetaData.reflect`.  By default,
							 | 
						||
| 
								 | 
							
								these operations exclude the ``SYSTEM``
							 | 
						||
| 
								 | 
							
								and ``SYSAUX`` tablespaces from the operation.   In order to change this, the
							 | 
						||
| 
								 | 
							
								default list of tablespaces excluded can be changed at the engine level using
							 | 
						||
| 
								 | 
							
								the ``exclude_tablespaces`` parameter::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    # exclude SYSAUX and SOME_TABLESPACE, but not SYSTEM
							 | 
						||
| 
								 | 
							
								    e = create_engine(
							 | 
						||
| 
								 | 
							
								      "oracle://scott:tiger@xe",
							 | 
						||
| 
								 | 
							
								      exclude_tablespaces=["SYSAUX", "SOME_TABLESPACE"])
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								.. versionadded:: 1.1
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								DateTime Compatibility
							 | 
						||
| 
								 | 
							
								----------------------
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Oracle has no datatype known as ``DATETIME``, it instead has only ``DATE``,
							 | 
						||
| 
								 | 
							
								which can actually store a date and time value.  For this reason, the Oracle
							 | 
						||
| 
								 | 
							
								dialect provides a type :class:`_oracle.DATE` which is a subclass of
							 | 
						||
| 
								 | 
							
								:class:`.DateTime`.   This type has no special behavior, and is only
							 | 
						||
| 
								 | 
							
								present as a "marker" for this type; additionally, when a database column
							 | 
						||
| 
								 | 
							
								is reflected and the type is reported as ``DATE``, the time-supporting
							 | 
						||
| 
								 | 
							
								:class:`_oracle.DATE` type is used.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								.. versionchanged:: 0.9.4 Added :class:`_oracle.DATE` to subclass
							 | 
						||
| 
								 | 
							
								   :class:`.DateTime`.  This is a change as previous versions
							 | 
						||
| 
								 | 
							
								   would reflect a ``DATE`` column as :class:`_types.DATE`, which subclasses
							 | 
						||
| 
								 | 
							
								   :class:`.Date`.   The only significance here is for schemes that are
							 | 
						||
| 
								 | 
							
								   examining the type of column for use in special Python translations or
							 | 
						||
| 
								 | 
							
								   for migrating schemas to other database backends.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								.. _oracle_table_options:
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Oracle Table Options
							 | 
						||
| 
								 | 
							
								-------------------------
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								The CREATE TABLE phrase supports the following options with Oracle
							 | 
						||
| 
								 | 
							
								in conjunction with the :class:`_schema.Table` construct:
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								* ``ON COMMIT``::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    Table(
							 | 
						||
| 
								 | 
							
								        "some_table", metadata, ...,
							 | 
						||
| 
								 | 
							
								        prefixes=['GLOBAL TEMPORARY'], oracle_on_commit='PRESERVE ROWS')
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								.. versionadded:: 1.0.0
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								* ``COMPRESS``::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    Table('mytable', metadata, Column('data', String(32)),
							 | 
						||
| 
								 | 
							
								        oracle_compress=True)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    Table('mytable', metadata, Column('data', String(32)),
							 | 
						||
| 
								 | 
							
								        oracle_compress=6)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								   The ``oracle_compress`` parameter accepts either an integer compression
							 | 
						||
| 
								 | 
							
								   level, or ``True`` to use the default compression level.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								.. versionadded:: 1.0.0
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								.. _oracle_index_options:
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Oracle Specific Index Options
							 | 
						||
| 
								 | 
							
								-----------------------------
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Bitmap Indexes
							 | 
						||
| 
								 | 
							
								~~~~~~~~~~~~~~
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								You can specify the ``oracle_bitmap`` parameter to create a bitmap index
							 | 
						||
| 
								 | 
							
								instead of a B-tree index::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    Index('my_index', my_table.c.data, oracle_bitmap=True)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Bitmap indexes cannot be unique and cannot be compressed. SQLAlchemy will not
							 | 
						||
| 
								 | 
							
								check for such limitations, only the database will.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								.. versionadded:: 1.0.0
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Index compression
							 | 
						||
| 
								 | 
							
								~~~~~~~~~~~~~~~~~
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Oracle has a more efficient storage mode for indexes containing lots of
							 | 
						||
| 
								 | 
							
								repeated values. Use the ``oracle_compress`` parameter to turn on key
							 | 
						||
| 
								 | 
							
								compression::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    Index('my_index', my_table.c.data, oracle_compress=True)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    Index('my_index', my_table.c.data1, my_table.c.data2, unique=True,
							 | 
						||
| 
								 | 
							
								           oracle_compress=1)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								The ``oracle_compress`` parameter accepts either an integer specifying the
							 | 
						||
| 
								 | 
							
								number of prefix columns to compress, or ``True`` to use the default (all
							 | 
						||
| 
								 | 
							
								columns for non-unique indexes, all but the last column for unique indexes).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								.. versionadded:: 1.0.0
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								"""  # noqa
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								from itertools import groupby
							 | 
						||
| 
								 | 
							
								import re
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								from ... import Computed
							 | 
						||
| 
								 | 
							
								from ... import exc
							 | 
						||
| 
								 | 
							
								from ... import schema as sa_schema
							 | 
						||
| 
								 | 
							
								from ... import sql
							 | 
						||
| 
								 | 
							
								from ... import util
							 | 
						||
| 
								 | 
							
								from ...engine import default
							 | 
						||
| 
								 | 
							
								from ...engine import reflection
							 | 
						||
| 
								 | 
							
								from ...sql import compiler
							 | 
						||
| 
								 | 
							
								from ...sql import expression
							 | 
						||
| 
								 | 
							
								from ...sql import sqltypes
							 | 
						||
| 
								 | 
							
								from ...sql import util as sql_util
							 | 
						||
| 
								 | 
							
								from ...sql import visitors
							 | 
						||
| 
								 | 
							
								from ...types import BLOB
							 | 
						||
| 
								 | 
							
								from ...types import CHAR
							 | 
						||
| 
								 | 
							
								from ...types import CLOB
							 | 
						||
| 
								 | 
							
								from ...types import FLOAT
							 | 
						||
| 
								 | 
							
								from ...types import INTEGER
							 | 
						||
| 
								 | 
							
								from ...types import NCHAR
							 | 
						||
| 
								 | 
							
								from ...types import NVARCHAR
							 | 
						||
| 
								 | 
							
								from ...types import TIMESTAMP
							 | 
						||
| 
								 | 
							
								from ...types import VARCHAR
							 | 
						||
| 
								 | 
							
								from ...util import compat
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								RESERVED_WORDS = set(
							 | 
						||
| 
								 | 
							
								    "SHARE RAW DROP BETWEEN FROM DESC OPTION PRIOR LONG THEN "
							 | 
						||
| 
								 | 
							
								    "DEFAULT ALTER IS INTO MINUS INTEGER NUMBER GRANT IDENTIFIED "
							 | 
						||
| 
								 | 
							
								    "ALL TO ORDER ON FLOAT DATE HAVING CLUSTER NOWAIT RESOURCE "
							 | 
						||
| 
								 | 
							
								    "ANY TABLE INDEX FOR UPDATE WHERE CHECK SMALLINT WITH DELETE "
							 | 
						||
| 
								 | 
							
								    "BY ASC REVOKE LIKE SIZE RENAME NOCOMPRESS NULL GROUP VALUES "
							 | 
						||
| 
								 | 
							
								    "AS IN VIEW EXCLUSIVE COMPRESS SYNONYM SELECT INSERT EXISTS "
							 | 
						||
| 
								 | 
							
								    "NOT TRIGGER ELSE CREATE INTERSECT PCTFREE DISTINCT USER "
							 | 
						||
| 
								 | 
							
								    "CONNECT SET MODE OF UNIQUE VARCHAR2 VARCHAR LOCK OR CHAR "
							 | 
						||
| 
								 | 
							
								    "DECIMAL UNION PUBLIC AND START UID COMMENT CURRENT LEVEL".split()
							 | 
						||
| 
								 | 
							
								)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								NO_ARG_FNS = set(
							 | 
						||
| 
								 | 
							
								    "UID CURRENT_DATE SYSDATE USER " "CURRENT_TIME CURRENT_TIMESTAMP".split()
							 | 
						||
| 
								 | 
							
								)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class RAW(sqltypes._Binary):
							 | 
						||
| 
								 | 
							
								    __visit_name__ = "RAW"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								OracleRaw = RAW
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class NCLOB(sqltypes.Text):
							 | 
						||
| 
								 | 
							
								    __visit_name__ = "NCLOB"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class VARCHAR2(VARCHAR):
							 | 
						||
| 
								 | 
							
								    __visit_name__ = "VARCHAR2"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								NVARCHAR2 = NVARCHAR
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class NUMBER(sqltypes.Numeric, sqltypes.Integer):
							 | 
						||
| 
								 | 
							
								    __visit_name__ = "NUMBER"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def __init__(self, precision=None, scale=None, asdecimal=None):
							 | 
						||
| 
								 | 
							
								        if asdecimal is None:
							 | 
						||
| 
								 | 
							
								            asdecimal = bool(scale and scale > 0)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        super(NUMBER, self).__init__(
							 | 
						||
| 
								 | 
							
								            precision=precision, scale=scale, asdecimal=asdecimal
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def adapt(self, impltype):
							 | 
						||
| 
								 | 
							
								        ret = super(NUMBER, self).adapt(impltype)
							 | 
						||
| 
								 | 
							
								        # leave a hint for the DBAPI handler
							 | 
						||
| 
								 | 
							
								        ret._is_oracle_number = True
							 | 
						||
| 
								 | 
							
								        return ret
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @property
							 | 
						||
| 
								 | 
							
								    def _type_affinity(self):
							 | 
						||
| 
								 | 
							
								        if bool(self.scale and self.scale > 0):
							 | 
						||
| 
								 | 
							
								            return sqltypes.Numeric
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            return sqltypes.Integer
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class DOUBLE_PRECISION(sqltypes.Float):
							 | 
						||
| 
								 | 
							
								    __visit_name__ = "DOUBLE_PRECISION"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class BINARY_DOUBLE(sqltypes.Float):
							 | 
						||
| 
								 | 
							
								    __visit_name__ = "BINARY_DOUBLE"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class BINARY_FLOAT(sqltypes.Float):
							 | 
						||
| 
								 | 
							
								    __visit_name__ = "BINARY_FLOAT"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class BFILE(sqltypes.LargeBinary):
							 | 
						||
| 
								 | 
							
								    __visit_name__ = "BFILE"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class LONG(sqltypes.Text):
							 | 
						||
| 
								 | 
							
								    __visit_name__ = "LONG"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class DATE(sqltypes.DateTime):
							 | 
						||
| 
								 | 
							
								    """Provide the oracle DATE type.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    This type has no special Python behavior, except that it subclasses
							 | 
						||
| 
								 | 
							
								    :class:`_types.DateTime`; this is to suit the fact that the Oracle
							 | 
						||
| 
								 | 
							
								    ``DATE`` type supports a time value.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    .. versionadded:: 0.9.4
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    """
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    __visit_name__ = "DATE"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def _compare_type_affinity(self, other):
							 | 
						||
| 
								 | 
							
								        return other._type_affinity in (sqltypes.DateTime, sqltypes.Date)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class INTERVAL(sqltypes.NativeForEmulated, sqltypes._AbstractInterval):
							 | 
						||
| 
								 | 
							
								    __visit_name__ = "INTERVAL"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def __init__(self, day_precision=None, second_precision=None):
							 | 
						||
| 
								 | 
							
								        """Construct an INTERVAL.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        Note that only DAY TO SECOND intervals are currently supported.
							 | 
						||
| 
								 | 
							
								        This is due to a lack of support for YEAR TO MONTH intervals
							 | 
						||
| 
								 | 
							
								        within available DBAPIs.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :param day_precision: the day precision value.  this is the number of
							 | 
						||
| 
								 | 
							
								          digits to store for the day field.  Defaults to "2"
							 | 
						||
| 
								 | 
							
								        :param second_precision: the second precision value.  this is the
							 | 
						||
| 
								 | 
							
								          number of digits to store for the fractional seconds field.
							 | 
						||
| 
								 | 
							
								          Defaults to "6".
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        self.day_precision = day_precision
							 | 
						||
| 
								 | 
							
								        self.second_precision = second_precision
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @classmethod
							 | 
						||
| 
								 | 
							
								    def _adapt_from_generic_interval(cls, interval):
							 | 
						||
| 
								 | 
							
								        return INTERVAL(
							 | 
						||
| 
								 | 
							
								            day_precision=interval.day_precision,
							 | 
						||
| 
								 | 
							
								            second_precision=interval.second_precision,
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @property
							 | 
						||
| 
								 | 
							
								    def _type_affinity(self):
							 | 
						||
| 
								 | 
							
								        return sqltypes.Interval
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def as_generic(self, allow_nulltype=False):
							 | 
						||
| 
								 | 
							
								        return sqltypes.Interval(
							 | 
						||
| 
								 | 
							
								            native=True,
							 | 
						||
| 
								 | 
							
								            second_precision=self.second_precision,
							 | 
						||
| 
								 | 
							
								            day_precision=self.day_precision,
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def coerce_compared_value(self, op, value):
							 | 
						||
| 
								 | 
							
								        return self
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class ROWID(sqltypes.TypeEngine):
							 | 
						||
| 
								 | 
							
								    """Oracle ROWID type.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    When used in a cast() or similar, generates ROWID.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    """
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    __visit_name__ = "ROWID"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class _OracleBoolean(sqltypes.Boolean):
							 | 
						||
| 
								 | 
							
								    def get_dbapi_type(self, dbapi):
							 | 
						||
| 
								 | 
							
								        return dbapi.NUMBER
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								colspecs = {
							 | 
						||
| 
								 | 
							
								    sqltypes.Boolean: _OracleBoolean,
							 | 
						||
| 
								 | 
							
								    sqltypes.Interval: INTERVAL,
							 | 
						||
| 
								 | 
							
								    sqltypes.DateTime: DATE,
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								ischema_names = {
							 | 
						||
| 
								 | 
							
								    "VARCHAR2": VARCHAR,
							 | 
						||
| 
								 | 
							
								    "NVARCHAR2": NVARCHAR,
							 | 
						||
| 
								 | 
							
								    "CHAR": CHAR,
							 | 
						||
| 
								 | 
							
								    "NCHAR": NCHAR,
							 | 
						||
| 
								 | 
							
								    "DATE": DATE,
							 | 
						||
| 
								 | 
							
								    "NUMBER": NUMBER,
							 | 
						||
| 
								 | 
							
								    "BLOB": BLOB,
							 | 
						||
| 
								 | 
							
								    "BFILE": BFILE,
							 | 
						||
| 
								 | 
							
								    "CLOB": CLOB,
							 | 
						||
| 
								 | 
							
								    "NCLOB": NCLOB,
							 | 
						||
| 
								 | 
							
								    "TIMESTAMP": TIMESTAMP,
							 | 
						||
| 
								 | 
							
								    "TIMESTAMP WITH TIME ZONE": TIMESTAMP,
							 | 
						||
| 
								 | 
							
								    "INTERVAL DAY TO SECOND": INTERVAL,
							 | 
						||
| 
								 | 
							
								    "RAW": RAW,
							 | 
						||
| 
								 | 
							
								    "FLOAT": FLOAT,
							 | 
						||
| 
								 | 
							
								    "DOUBLE PRECISION": DOUBLE_PRECISION,
							 | 
						||
| 
								 | 
							
								    "LONG": LONG,
							 | 
						||
| 
								 | 
							
								    "BINARY_DOUBLE": BINARY_DOUBLE,
							 | 
						||
| 
								 | 
							
								    "BINARY_FLOAT": BINARY_FLOAT,
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class OracleTypeCompiler(compiler.GenericTypeCompiler):
							 | 
						||
| 
								 | 
							
								    # Note:
							 | 
						||
| 
								 | 
							
								    # Oracle DATE == DATETIME
							 | 
						||
| 
								 | 
							
								    # Oracle does not allow milliseconds in DATE
							 | 
						||
| 
								 | 
							
								    # Oracle does not support TIME columns
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def visit_datetime(self, type_, **kw):
							 | 
						||
| 
								 | 
							
								        return self.visit_DATE(type_, **kw)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def visit_float(self, type_, **kw):
							 | 
						||
| 
								 | 
							
								        return self.visit_FLOAT(type_, **kw)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def visit_unicode(self, type_, **kw):
							 | 
						||
| 
								 | 
							
								        if self.dialect._use_nchar_for_unicode:
							 | 
						||
| 
								 | 
							
								            return self.visit_NVARCHAR2(type_, **kw)
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            return self.visit_VARCHAR2(type_, **kw)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def visit_INTERVAL(self, type_, **kw):
							 | 
						||
| 
								 | 
							
								        return "INTERVAL DAY%s TO SECOND%s" % (
							 | 
						||
| 
								 | 
							
								            type_.day_precision is not None
							 | 
						||
| 
								 | 
							
								            and "(%d)" % type_.day_precision
							 | 
						||
| 
								 | 
							
								            or "",
							 | 
						||
| 
								 | 
							
								            type_.second_precision is not None
							 | 
						||
| 
								 | 
							
								            and "(%d)" % type_.second_precision
							 | 
						||
| 
								 | 
							
								            or "",
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def visit_LONG(self, type_, **kw):
							 | 
						||
| 
								 | 
							
								        return "LONG"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def visit_TIMESTAMP(self, type_, **kw):
							 | 
						||
| 
								 | 
							
								        if type_.timezone:
							 | 
						||
| 
								 | 
							
								            return "TIMESTAMP WITH TIME ZONE"
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            return "TIMESTAMP"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def visit_DOUBLE_PRECISION(self, type_, **kw):
							 | 
						||
| 
								 | 
							
								        return self._generate_numeric(type_, "DOUBLE PRECISION", **kw)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def visit_BINARY_DOUBLE(self, type_, **kw):
							 | 
						||
| 
								 | 
							
								        return self._generate_numeric(type_, "BINARY_DOUBLE", **kw)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def visit_BINARY_FLOAT(self, type_, **kw):
							 | 
						||
| 
								 | 
							
								        return self._generate_numeric(type_, "BINARY_FLOAT", **kw)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def visit_FLOAT(self, type_, **kw):
							 | 
						||
| 
								 | 
							
								        # don't support conversion between decimal/binary
							 | 
						||
| 
								 | 
							
								        # precision yet
							 | 
						||
| 
								 | 
							
								        kw["no_precision"] = True
							 | 
						||
| 
								 | 
							
								        return self._generate_numeric(type_, "FLOAT", **kw)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def visit_NUMBER(self, type_, **kw):
							 | 
						||
| 
								 | 
							
								        return self._generate_numeric(type_, "NUMBER", **kw)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def _generate_numeric(
							 | 
						||
| 
								 | 
							
								        self, type_, name, precision=None, scale=None, no_precision=False, **kw
							 | 
						||
| 
								 | 
							
								    ):
							 | 
						||
| 
								 | 
							
								        if precision is None:
							 | 
						||
| 
								 | 
							
								            precision = type_.precision
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if scale is None:
							 | 
						||
| 
								 | 
							
								            scale = getattr(type_, "scale", None)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if no_precision or precision is None:
							 | 
						||
| 
								 | 
							
								            return name
							 | 
						||
| 
								 | 
							
								        elif scale is None:
							 | 
						||
| 
								 | 
							
								            n = "%(name)s(%(precision)s)"
							 | 
						||
| 
								 | 
							
								            return n % {"name": name, "precision": precision}
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            n = "%(name)s(%(precision)s, %(scale)s)"
							 | 
						||
| 
								 | 
							
								            return n % {"name": name, "precision": precision, "scale": scale}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def visit_string(self, type_, **kw):
							 | 
						||
| 
								 | 
							
								        return self.visit_VARCHAR2(type_, **kw)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def visit_VARCHAR2(self, type_, **kw):
							 | 
						||
| 
								 | 
							
								        return self._visit_varchar(type_, "", "2")
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def visit_NVARCHAR2(self, type_, **kw):
							 | 
						||
| 
								 | 
							
								        return self._visit_varchar(type_, "N", "2")
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    visit_NVARCHAR = visit_NVARCHAR2
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def visit_VARCHAR(self, type_, **kw):
							 | 
						||
| 
								 | 
							
								        return self._visit_varchar(type_, "", "")
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def _visit_varchar(self, type_, n, num):
							 | 
						||
| 
								 | 
							
								        if not type_.length:
							 | 
						||
| 
								 | 
							
								            return "%(n)sVARCHAR%(two)s" % {"two": num, "n": n}
							 | 
						||
| 
								 | 
							
								        elif not n and self.dialect._supports_char_length:
							 | 
						||
| 
								 | 
							
								            varchar = "VARCHAR%(two)s(%(length)s CHAR)"
							 | 
						||
| 
								 | 
							
								            return varchar % {"length": type_.length, "two": num}
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            varchar = "%(n)sVARCHAR%(two)s(%(length)s)"
							 | 
						||
| 
								 | 
							
								            return varchar % {"length": type_.length, "two": num, "n": n}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def visit_text(self, type_, **kw):
							 | 
						||
| 
								 | 
							
								        return self.visit_CLOB(type_, **kw)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def visit_unicode_text(self, type_, **kw):
							 | 
						||
| 
								 | 
							
								        if self.dialect._use_nchar_for_unicode:
							 | 
						||
| 
								 | 
							
								            return self.visit_NCLOB(type_, **kw)
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            return self.visit_CLOB(type_, **kw)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def visit_large_binary(self, type_, **kw):
							 | 
						||
| 
								 | 
							
								        return self.visit_BLOB(type_, **kw)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def visit_big_integer(self, type_, **kw):
							 | 
						||
| 
								 | 
							
								        return self.visit_NUMBER(type_, precision=19, **kw)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def visit_boolean(self, type_, **kw):
							 | 
						||
| 
								 | 
							
								        return self.visit_SMALLINT(type_, **kw)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def visit_RAW(self, type_, **kw):
							 | 
						||
| 
								 | 
							
								        if type_.length:
							 | 
						||
| 
								 | 
							
								            return "RAW(%(length)s)" % {"length": type_.length}
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            return "RAW"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def visit_ROWID(self, type_, **kw):
							 | 
						||
| 
								 | 
							
								        return "ROWID"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class OracleCompiler(compiler.SQLCompiler):
							 | 
						||
| 
								 | 
							
								    """Oracle compiler modifies the lexical structure of Select
							 | 
						||
| 
								 | 
							
								    statements to work under non-ANSI configured Oracle databases, if
							 | 
						||
| 
								 | 
							
								    the use_ansi flag is False.
							 | 
						||
| 
								 | 
							
								    """
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    compound_keywords = util.update_copy(
							 | 
						||
| 
								 | 
							
								        compiler.SQLCompiler.compound_keywords,
							 | 
						||
| 
								 | 
							
								        {expression.CompoundSelect.EXCEPT: "MINUS"},
							 | 
						||
| 
								 | 
							
								    )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def __init__(self, *args, **kwargs):
							 | 
						||
| 
								 | 
							
								        self.__wheres = {}
							 | 
						||
| 
								 | 
							
								        super(OracleCompiler, self).__init__(*args, **kwargs)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def visit_mod_binary(self, binary, operator, **kw):
							 | 
						||
| 
								 | 
							
								        return "mod(%s, %s)" % (
							 | 
						||
| 
								 | 
							
								            self.process(binary.left, **kw),
							 | 
						||
| 
								 | 
							
								            self.process(binary.right, **kw),
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def visit_now_func(self, fn, **kw):
							 | 
						||
| 
								 | 
							
								        return "CURRENT_TIMESTAMP"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def visit_char_length_func(self, fn, **kw):
							 | 
						||
| 
								 | 
							
								        return "LENGTH" + self.function_argspec(fn, **kw)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def visit_match_op_binary(self, binary, operator, **kw):
							 | 
						||
| 
								 | 
							
								        return "CONTAINS (%s, %s)" % (
							 | 
						||
| 
								 | 
							
								            self.process(binary.left),
							 | 
						||
| 
								 | 
							
								            self.process(binary.right),
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def visit_true(self, expr, **kw):
							 | 
						||
| 
								 | 
							
								        return "1"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def visit_false(self, expr, **kw):
							 | 
						||
| 
								 | 
							
								        return "0"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def get_cte_preamble(self, recursive):
							 | 
						||
| 
								 | 
							
								        return "WITH"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def get_select_hint_text(self, byfroms):
							 | 
						||
| 
								 | 
							
								        return " ".join("/*+ %s */" % text for table, text in byfroms.items())
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def function_argspec(self, fn, **kw):
							 | 
						||
| 
								 | 
							
								        if len(fn.clauses) > 0 or fn.name.upper() not in NO_ARG_FNS:
							 | 
						||
| 
								 | 
							
								            return compiler.SQLCompiler.function_argspec(self, fn, **kw)
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            return ""
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def visit_function(self, func, **kw):
							 | 
						||
| 
								 | 
							
								        text = super(OracleCompiler, self).visit_function(func, **kw)
							 | 
						||
| 
								 | 
							
								        if kw.get("asfrom", False):
							 | 
						||
| 
								 | 
							
								            text = "TABLE (%s)" % func
							 | 
						||
| 
								 | 
							
								        return text
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def visit_table_valued_column(self, element, **kw):
							 | 
						||
| 
								 | 
							
								        text = super(OracleCompiler, self).visit_table_valued_column(
							 | 
						||
| 
								 | 
							
								            element, **kw
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								        text = "COLUMN_VALUE " + text
							 | 
						||
| 
								 | 
							
								        return text
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def default_from(self):
							 | 
						||
| 
								 | 
							
								        """Called when a ``SELECT`` statement has no froms,
							 | 
						||
| 
								 | 
							
								        and no ``FROM`` clause is to be appended.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        The Oracle compiler tacks a "FROM DUAL" to the statement.
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        return " FROM DUAL"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def visit_join(self, join, from_linter=None, **kwargs):
							 | 
						||
| 
								 | 
							
								        if self.dialect.use_ansi:
							 | 
						||
| 
								 | 
							
								            return compiler.SQLCompiler.visit_join(
							 | 
						||
| 
								 | 
							
								                self, join, from_linter=from_linter, **kwargs
							 | 
						||
| 
								 | 
							
								            )
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            if from_linter:
							 | 
						||
| 
								 | 
							
								                from_linter.edges.add((join.left, join.right))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            kwargs["asfrom"] = True
							 | 
						||
| 
								 | 
							
								            if isinstance(join.right, expression.FromGrouping):
							 | 
						||
| 
								 | 
							
								                right = join.right.element
							 | 
						||
| 
								 | 
							
								            else:
							 | 
						||
| 
								 | 
							
								                right = join.right
							 | 
						||
| 
								 | 
							
								            return (
							 | 
						||
| 
								 | 
							
								                self.process(join.left, from_linter=from_linter, **kwargs)
							 | 
						||
| 
								 | 
							
								                + ", "
							 | 
						||
| 
								 | 
							
								                + self.process(right, from_linter=from_linter, **kwargs)
							 | 
						||
| 
								 | 
							
								            )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def _get_nonansi_join_whereclause(self, froms):
							 | 
						||
| 
								 | 
							
								        clauses = []
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        def visit_join(join):
							 | 
						||
| 
								 | 
							
								            if join.isouter:
							 | 
						||
| 
								 | 
							
								                # https://docs.oracle.com/database/121/SQLRF/queries006.htm#SQLRF52354
							 | 
						||
| 
								 | 
							
								                # "apply the outer join operator (+) to all columns of B in
							 | 
						||
| 
								 | 
							
								                # the join condition in the WHERE clause" - that is,
							 | 
						||
| 
								 | 
							
								                # unconditionally regardless of operator or the other side
							 | 
						||
| 
								 | 
							
								                def visit_binary(binary):
							 | 
						||
| 
								 | 
							
								                    if isinstance(
							 | 
						||
| 
								 | 
							
								                        binary.left, expression.ColumnClause
							 | 
						||
| 
								 | 
							
								                    ) and join.right.is_derived_from(binary.left.table):
							 | 
						||
| 
								 | 
							
								                        binary.left = _OuterJoinColumn(binary.left)
							 | 
						||
| 
								 | 
							
								                    elif isinstance(
							 | 
						||
| 
								 | 
							
								                        binary.right, expression.ColumnClause
							 | 
						||
| 
								 | 
							
								                    ) and join.right.is_derived_from(binary.right.table):
							 | 
						||
| 
								 | 
							
								                        binary.right = _OuterJoinColumn(binary.right)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                clauses.append(
							 | 
						||
| 
								 | 
							
								                    visitors.cloned_traverse(
							 | 
						||
| 
								 | 
							
								                        join.onclause, {}, {"binary": visit_binary}
							 | 
						||
| 
								 | 
							
								                    )
							 | 
						||
| 
								 | 
							
								                )
							 | 
						||
| 
								 | 
							
								            else:
							 | 
						||
| 
								 | 
							
								                clauses.append(join.onclause)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            for j in join.left, join.right:
							 | 
						||
| 
								 | 
							
								                if isinstance(j, expression.Join):
							 | 
						||
| 
								 | 
							
								                    visit_join(j)
							 | 
						||
| 
								 | 
							
								                elif isinstance(j, expression.FromGrouping):
							 | 
						||
| 
								 | 
							
								                    visit_join(j.element)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        for f in froms:
							 | 
						||
| 
								 | 
							
								            if isinstance(f, expression.Join):
							 | 
						||
| 
								 | 
							
								                visit_join(f)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if not clauses:
							 | 
						||
| 
								 | 
							
								            return None
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            return sql.and_(*clauses)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def visit_outer_join_column(self, vc, **kw):
							 | 
						||
| 
								 | 
							
								        return self.process(vc.column, **kw) + "(+)"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def visit_sequence(self, seq, **kw):
							 | 
						||
| 
								 | 
							
								        return self.preparer.format_sequence(seq) + ".nextval"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def get_render_as_alias_suffix(self, alias_name_text):
							 | 
						||
| 
								 | 
							
								        """Oracle doesn't like ``FROM table AS alias``"""
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        return " " + alias_name_text
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def returning_clause(self, stmt, returning_cols):
							 | 
						||
| 
								 | 
							
								        columns = []
							 | 
						||
| 
								 | 
							
								        binds = []
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        for i, column in enumerate(
							 | 
						||
| 
								 | 
							
								            expression._select_iterables(returning_cols)
							 | 
						||
| 
								 | 
							
								        ):
							 | 
						||
| 
								 | 
							
								            if (
							 | 
						||
| 
								 | 
							
								                self.isupdate
							 | 
						||
| 
								 | 
							
								                and isinstance(column, sa_schema.Column)
							 | 
						||
| 
								 | 
							
								                and isinstance(column.server_default, Computed)
							 | 
						||
| 
								 | 
							
								                and not self.dialect._supports_update_returning_computed_cols
							 | 
						||
| 
								 | 
							
								            ):
							 | 
						||
| 
								 | 
							
								                util.warn(
							 | 
						||
| 
								 | 
							
								                    "Computed columns don't work with Oracle UPDATE "
							 | 
						||
| 
								 | 
							
								                    "statements that use RETURNING; the value of the column "
							 | 
						||
| 
								 | 
							
								                    "*before* the UPDATE takes place is returned.   It is "
							 | 
						||
| 
								 | 
							
								                    "advised to not use RETURNING with an Oracle computed "
							 | 
						||
| 
								 | 
							
								                    "column.  Consider setting implicit_returning to False on "
							 | 
						||
| 
								 | 
							
								                    "the Table object in order to avoid implicit RETURNING "
							 | 
						||
| 
								 | 
							
								                    "clauses from being generated for this Table."
							 | 
						||
| 
								 | 
							
								                )
							 | 
						||
| 
								 | 
							
								            if column.type._has_column_expression:
							 | 
						||
| 
								 | 
							
								                col_expr = column.type.column_expression(column)
							 | 
						||
| 
								 | 
							
								            else:
							 | 
						||
| 
								 | 
							
								                col_expr = column
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            outparam = sql.outparam("ret_%d" % i, type_=column.type)
							 | 
						||
| 
								 | 
							
								            self.binds[outparam.key] = outparam
							 | 
						||
| 
								 | 
							
								            binds.append(
							 | 
						||
| 
								 | 
							
								                self.bindparam_string(self._truncate_bindparam(outparam))
							 | 
						||
| 
								 | 
							
								            )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            # ensure the ExecutionContext.get_out_parameters() method is
							 | 
						||
| 
								 | 
							
								            # *not* called; the cx_Oracle dialect wants to handle these
							 | 
						||
| 
								 | 
							
								            # parameters separately
							 | 
						||
| 
								 | 
							
								            self.has_out_parameters = False
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            columns.append(self.process(col_expr, within_columns_clause=False))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            self._add_to_result_map(
							 | 
						||
| 
								 | 
							
								                getattr(col_expr, "name", col_expr._anon_name_label),
							 | 
						||
| 
								 | 
							
								                getattr(col_expr, "name", col_expr._anon_name_label),
							 | 
						||
| 
								 | 
							
								                (
							 | 
						||
| 
								 | 
							
								                    column,
							 | 
						||
| 
								 | 
							
								                    getattr(column, "name", None),
							 | 
						||
| 
								 | 
							
								                    getattr(column, "key", None),
							 | 
						||
| 
								 | 
							
								                ),
							 | 
						||
| 
								 | 
							
								                column.type,
							 | 
						||
| 
								 | 
							
								            )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        return "RETURNING " + ", ".join(columns) + " INTO " + ", ".join(binds)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def translate_select_structure(self, select_stmt, **kwargs):
							 | 
						||
| 
								 | 
							
								        select = select_stmt
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if not getattr(select, "_oracle_visit", None):
							 | 
						||
| 
								 | 
							
								            if not self.dialect.use_ansi:
							 | 
						||
| 
								 | 
							
								                froms = self._display_froms_for_select(
							 | 
						||
| 
								 | 
							
								                    select, kwargs.get("asfrom", False)
							 | 
						||
| 
								 | 
							
								                )
							 | 
						||
| 
								 | 
							
								                whereclause = self._get_nonansi_join_whereclause(froms)
							 | 
						||
| 
								 | 
							
								                if whereclause is not None:
							 | 
						||
| 
								 | 
							
								                    select = select.where(whereclause)
							 | 
						||
| 
								 | 
							
								                    select._oracle_visit = True
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            # if fetch is used this is not needed
							 | 
						||
| 
								 | 
							
								            if (
							 | 
						||
| 
								 | 
							
								                select._has_row_limiting_clause
							 | 
						||
| 
								 | 
							
								                and select._fetch_clause is None
							 | 
						||
| 
								 | 
							
								            ):
							 | 
						||
| 
								 | 
							
								                limit_clause = select._limit_clause
							 | 
						||
| 
								 | 
							
								                offset_clause = select._offset_clause
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                if select._simple_int_clause(limit_clause):
							 | 
						||
| 
								 | 
							
								                    limit_clause = limit_clause.render_literal_execute()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                if select._simple_int_clause(offset_clause):
							 | 
						||
| 
								 | 
							
								                    offset_clause = offset_clause.render_literal_execute()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                # currently using form at:
							 | 
						||
| 
								 | 
							
								                # https://blogs.oracle.com/oraclemagazine/\
							 | 
						||
| 
								 | 
							
								                # on-rownum-and-limiting-results
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                orig_select = select
							 | 
						||
| 
								 | 
							
								                select = select._generate()
							 | 
						||
| 
								 | 
							
								                select._oracle_visit = True
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                # add expressions to accommodate FOR UPDATE OF
							 | 
						||
| 
								 | 
							
								                for_update = select._for_update_arg
							 | 
						||
| 
								 | 
							
								                if for_update is not None and for_update.of:
							 | 
						||
| 
								 | 
							
								                    for_update = for_update._clone()
							 | 
						||
| 
								 | 
							
								                    for_update._copy_internals()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                    for elem in for_update.of:
							 | 
						||
| 
								 | 
							
								                        if not select.selected_columns.contains_column(elem):
							 | 
						||
| 
								 | 
							
								                            select = select.add_columns(elem)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                # Wrap the middle select and add the hint
							 | 
						||
| 
								 | 
							
								                inner_subquery = select.alias()
							 | 
						||
| 
								 | 
							
								                limitselect = sql.select(
							 | 
						||
| 
								 | 
							
								                    *[
							 | 
						||
| 
								 | 
							
								                        c
							 | 
						||
| 
								 | 
							
								                        for c in inner_subquery.c
							 | 
						||
| 
								 | 
							
								                        if orig_select.selected_columns.corresponding_column(c)
							 | 
						||
| 
								 | 
							
								                        is not None
							 | 
						||
| 
								 | 
							
								                    ]
							 | 
						||
| 
								 | 
							
								                )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                if (
							 | 
						||
| 
								 | 
							
								                    limit_clause is not None
							 | 
						||
| 
								 | 
							
								                    and self.dialect.optimize_limits
							 | 
						||
| 
								 | 
							
								                    and select._simple_int_clause(limit_clause)
							 | 
						||
| 
								 | 
							
								                ):
							 | 
						||
| 
								 | 
							
								                    limitselect = limitselect.prefix_with(
							 | 
						||
| 
								 | 
							
								                        expression.text(
							 | 
						||
| 
								 | 
							
								                            "/*+ FIRST_ROWS(%s) */"
							 | 
						||
| 
								 | 
							
								                            % self.process(limit_clause, **kwargs)
							 | 
						||
| 
								 | 
							
								                        )
							 | 
						||
| 
								 | 
							
								                    )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                limitselect._oracle_visit = True
							 | 
						||
| 
								 | 
							
								                limitselect._is_wrapper = True
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                # add expressions to accommodate FOR UPDATE OF
							 | 
						||
| 
								 | 
							
								                if for_update is not None and for_update.of:
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                    adapter = sql_util.ClauseAdapter(inner_subquery)
							 | 
						||
| 
								 | 
							
								                    for_update.of = [
							 | 
						||
| 
								 | 
							
								                        adapter.traverse(elem) for elem in for_update.of
							 | 
						||
| 
								 | 
							
								                    ]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                # If needed, add the limiting clause
							 | 
						||
| 
								 | 
							
								                if limit_clause is not None:
							 | 
						||
| 
								 | 
							
								                    if select._simple_int_clause(limit_clause) and (
							 | 
						||
| 
								 | 
							
								                        offset_clause is None
							 | 
						||
| 
								 | 
							
								                        or select._simple_int_clause(offset_clause)
							 | 
						||
| 
								 | 
							
								                    ):
							 | 
						||
| 
								 | 
							
								                        max_row = limit_clause
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                        if offset_clause is not None:
							 | 
						||
| 
								 | 
							
								                            max_row = max_row + offset_clause
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                    else:
							 | 
						||
| 
								 | 
							
								                        max_row = limit_clause
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                        if offset_clause is not None:
							 | 
						||
| 
								 | 
							
								                            max_row = max_row + offset_clause
							 | 
						||
| 
								 | 
							
								                    limitselect = limitselect.where(
							 | 
						||
| 
								 | 
							
								                        sql.literal_column("ROWNUM") <= max_row
							 | 
						||
| 
								 | 
							
								                    )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                # If needed, add the ora_rn, and wrap again with offset.
							 | 
						||
| 
								 | 
							
								                if offset_clause is None:
							 | 
						||
| 
								 | 
							
								                    limitselect._for_update_arg = for_update
							 | 
						||
| 
								 | 
							
								                    select = limitselect
							 | 
						||
| 
								 | 
							
								                else:
							 | 
						||
| 
								 | 
							
								                    limitselect = limitselect.add_columns(
							 | 
						||
| 
								 | 
							
								                        sql.literal_column("ROWNUM").label("ora_rn")
							 | 
						||
| 
								 | 
							
								                    )
							 | 
						||
| 
								 | 
							
								                    limitselect._oracle_visit = True
							 | 
						||
| 
								 | 
							
								                    limitselect._is_wrapper = True
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                    if for_update is not None and for_update.of:
							 | 
						||
| 
								 | 
							
								                        limitselect_cols = limitselect.selected_columns
							 | 
						||
| 
								 | 
							
								                        for elem in for_update.of:
							 | 
						||
| 
								 | 
							
								                            if (
							 | 
						||
| 
								 | 
							
								                                limitselect_cols.corresponding_column(elem)
							 | 
						||
| 
								 | 
							
								                                is None
							 | 
						||
| 
								 | 
							
								                            ):
							 | 
						||
| 
								 | 
							
								                                limitselect = limitselect.add_columns(elem)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                    limit_subquery = limitselect.alias()
							 | 
						||
| 
								 | 
							
								                    origselect_cols = orig_select.selected_columns
							 | 
						||
| 
								 | 
							
								                    offsetselect = sql.select(
							 | 
						||
| 
								 | 
							
								                        *[
							 | 
						||
| 
								 | 
							
								                            c
							 | 
						||
| 
								 | 
							
								                            for c in limit_subquery.c
							 | 
						||
| 
								 | 
							
								                            if origselect_cols.corresponding_column(c)
							 | 
						||
| 
								 | 
							
								                            is not None
							 | 
						||
| 
								 | 
							
								                        ]
							 | 
						||
| 
								 | 
							
								                    )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                    offsetselect._oracle_visit = True
							 | 
						||
| 
								 | 
							
								                    offsetselect._is_wrapper = True
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                    if for_update is not None and for_update.of:
							 | 
						||
| 
								 | 
							
								                        adapter = sql_util.ClauseAdapter(limit_subquery)
							 | 
						||
| 
								 | 
							
								                        for_update.of = [
							 | 
						||
| 
								 | 
							
								                            adapter.traverse(elem) for elem in for_update.of
							 | 
						||
| 
								 | 
							
								                        ]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                    offsetselect = offsetselect.where(
							 | 
						||
| 
								 | 
							
								                        sql.literal_column("ora_rn") > offset_clause
							 | 
						||
| 
								 | 
							
								                    )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                    offsetselect._for_update_arg = for_update
							 | 
						||
| 
								 | 
							
								                    select = offsetselect
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        return select
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def limit_clause(self, select, **kw):
							 | 
						||
| 
								 | 
							
								        return ""
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def visit_empty_set_expr(self, type_):
							 | 
						||
| 
								 | 
							
								        return "SELECT 1 FROM DUAL WHERE 1!=1"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def for_update_clause(self, select, **kw):
							 | 
						||
| 
								 | 
							
								        if self.is_subquery():
							 | 
						||
| 
								 | 
							
								            return ""
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        tmp = " FOR UPDATE"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if select._for_update_arg.of:
							 | 
						||
| 
								 | 
							
								            tmp += " OF " + ", ".join(
							 | 
						||
| 
								 | 
							
								                self.process(elem, **kw) for elem in select._for_update_arg.of
							 | 
						||
| 
								 | 
							
								            )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if select._for_update_arg.nowait:
							 | 
						||
| 
								 | 
							
								            tmp += " NOWAIT"
							 | 
						||
| 
								 | 
							
								        if select._for_update_arg.skip_locked:
							 | 
						||
| 
								 | 
							
								            tmp += " SKIP LOCKED"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        return tmp
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def visit_is_distinct_from_binary(self, binary, operator, **kw):
							 | 
						||
| 
								 | 
							
								        return "DECODE(%s, %s, 0, 1) = 1" % (
							 | 
						||
| 
								 | 
							
								            self.process(binary.left),
							 | 
						||
| 
								 | 
							
								            self.process(binary.right),
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def visit_is_not_distinct_from_binary(self, binary, operator, **kw):
							 | 
						||
| 
								 | 
							
								        return "DECODE(%s, %s, 0, 1) = 0" % (
							 | 
						||
| 
								 | 
							
								            self.process(binary.left),
							 | 
						||
| 
								 | 
							
								            self.process(binary.right),
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def _get_regexp_args(self, binary, kw):
							 | 
						||
| 
								 | 
							
								        string = self.process(binary.left, **kw)
							 | 
						||
| 
								 | 
							
								        pattern = self.process(binary.right, **kw)
							 | 
						||
| 
								 | 
							
								        flags = binary.modifiers["flags"]
							 | 
						||
| 
								 | 
							
								        if flags is not None:
							 | 
						||
| 
								 | 
							
								            flags = self.process(flags, **kw)
							 | 
						||
| 
								 | 
							
								        return string, pattern, flags
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def visit_regexp_match_op_binary(self, binary, operator, **kw):
							 | 
						||
| 
								 | 
							
								        string, pattern, flags = self._get_regexp_args(binary, kw)
							 | 
						||
| 
								 | 
							
								        if flags is None:
							 | 
						||
| 
								 | 
							
								            return "REGEXP_LIKE(%s, %s)" % (string, pattern)
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            return "REGEXP_LIKE(%s, %s, %s)" % (string, pattern, flags)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def visit_not_regexp_match_op_binary(self, binary, operator, **kw):
							 | 
						||
| 
								 | 
							
								        return "NOT %s" % self.visit_regexp_match_op_binary(
							 | 
						||
| 
								 | 
							
								            binary, operator, **kw
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def visit_regexp_replace_op_binary(self, binary, operator, **kw):
							 | 
						||
| 
								 | 
							
								        string, pattern, flags = self._get_regexp_args(binary, kw)
							 | 
						||
| 
								 | 
							
								        replacement = self.process(binary.modifiers["replacement"], **kw)
							 | 
						||
| 
								 | 
							
								        if flags is None:
							 | 
						||
| 
								 | 
							
								            return "REGEXP_REPLACE(%s, %s, %s)" % (
							 | 
						||
| 
								 | 
							
								                string,
							 | 
						||
| 
								 | 
							
								                pattern,
							 | 
						||
| 
								 | 
							
								                replacement,
							 | 
						||
| 
								 | 
							
								            )
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            return "REGEXP_REPLACE(%s, %s, %s, %s)" % (
							 | 
						||
| 
								 | 
							
								                string,
							 | 
						||
| 
								 | 
							
								                pattern,
							 | 
						||
| 
								 | 
							
								                replacement,
							 | 
						||
| 
								 | 
							
								                flags,
							 | 
						||
| 
								 | 
							
								            )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class OracleDDLCompiler(compiler.DDLCompiler):
							 | 
						||
| 
								 | 
							
								    def define_constraint_cascades(self, constraint):
							 | 
						||
| 
								 | 
							
								        text = ""
							 | 
						||
| 
								 | 
							
								        if constraint.ondelete is not None:
							 | 
						||
| 
								 | 
							
								            text += " ON DELETE %s" % constraint.ondelete
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # oracle has no ON UPDATE CASCADE -
							 | 
						||
| 
								 | 
							
								        # its only available via triggers
							 | 
						||
| 
								 | 
							
								        # https://asktom.oracle.com/tkyte/update_cascade/index.html
							 | 
						||
| 
								 | 
							
								        if constraint.onupdate is not None:
							 | 
						||
| 
								 | 
							
								            util.warn(
							 | 
						||
| 
								 | 
							
								                "Oracle does not contain native UPDATE CASCADE "
							 | 
						||
| 
								 | 
							
								                "functionality - onupdates will not be rendered for foreign "
							 | 
						||
| 
								 | 
							
								                "keys.  Consider using deferrable=True, initially='deferred' "
							 | 
						||
| 
								 | 
							
								                "or triggers."
							 | 
						||
| 
								 | 
							
								            )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        return text
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def visit_drop_table_comment(self, drop):
							 | 
						||
| 
								 | 
							
								        return "COMMENT ON TABLE %s IS ''" % self.preparer.format_table(
							 | 
						||
| 
								 | 
							
								            drop.element
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def visit_create_index(self, create):
							 | 
						||
| 
								 | 
							
								        index = create.element
							 | 
						||
| 
								 | 
							
								        self._verify_index_table(index)
							 | 
						||
| 
								 | 
							
								        preparer = self.preparer
							 | 
						||
| 
								 | 
							
								        text = "CREATE "
							 | 
						||
| 
								 | 
							
								        if index.unique:
							 | 
						||
| 
								 | 
							
								            text += "UNIQUE "
							 | 
						||
| 
								 | 
							
								        if index.dialect_options["oracle"]["bitmap"]:
							 | 
						||
| 
								 | 
							
								            text += "BITMAP "
							 | 
						||
| 
								 | 
							
								        text += "INDEX %s ON %s (%s)" % (
							 | 
						||
| 
								 | 
							
								            self._prepared_index_name(index, include_schema=True),
							 | 
						||
| 
								 | 
							
								            preparer.format_table(index.table, use_schema=True),
							 | 
						||
| 
								 | 
							
								            ", ".join(
							 | 
						||
| 
								 | 
							
								                self.sql_compiler.process(
							 | 
						||
| 
								 | 
							
								                    expr, include_table=False, literal_binds=True
							 | 
						||
| 
								 | 
							
								                )
							 | 
						||
| 
								 | 
							
								                for expr in index.expressions
							 | 
						||
| 
								 | 
							
								            ),
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								        if index.dialect_options["oracle"]["compress"] is not False:
							 | 
						||
| 
								 | 
							
								            if index.dialect_options["oracle"]["compress"] is True:
							 | 
						||
| 
								 | 
							
								                text += " COMPRESS"
							 | 
						||
| 
								 | 
							
								            else:
							 | 
						||
| 
								 | 
							
								                text += " COMPRESS %d" % (
							 | 
						||
| 
								 | 
							
								                    index.dialect_options["oracle"]["compress"]
							 | 
						||
| 
								 | 
							
								                )
							 | 
						||
| 
								 | 
							
								        return text
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def post_create_table(self, table):
							 | 
						||
| 
								 | 
							
								        table_opts = []
							 | 
						||
| 
								 | 
							
								        opts = table.dialect_options["oracle"]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if opts["on_commit"]:
							 | 
						||
| 
								 | 
							
								            on_commit_options = opts["on_commit"].replace("_", " ").upper()
							 | 
						||
| 
								 | 
							
								            table_opts.append("\n ON COMMIT %s" % on_commit_options)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if opts["compress"]:
							 | 
						||
| 
								 | 
							
								            if opts["compress"] is True:
							 | 
						||
| 
								 | 
							
								                table_opts.append("\n COMPRESS")
							 | 
						||
| 
								 | 
							
								            else:
							 | 
						||
| 
								 | 
							
								                table_opts.append("\n COMPRESS FOR %s" % (opts["compress"]))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        return "".join(table_opts)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def get_identity_options(self, identity_options):
							 | 
						||
| 
								 | 
							
								        text = super(OracleDDLCompiler, self).get_identity_options(
							 | 
						||
| 
								 | 
							
								            identity_options
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								        text = text.replace("NO MINVALUE", "NOMINVALUE")
							 | 
						||
| 
								 | 
							
								        text = text.replace("NO MAXVALUE", "NOMAXVALUE")
							 | 
						||
| 
								 | 
							
								        text = text.replace("NO CYCLE", "NOCYCLE")
							 | 
						||
| 
								 | 
							
								        text = text.replace("NO ORDER", "NOORDER")
							 | 
						||
| 
								 | 
							
								        return text
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def visit_computed_column(self, generated):
							 | 
						||
| 
								 | 
							
								        text = "GENERATED ALWAYS AS (%s)" % self.sql_compiler.process(
							 | 
						||
| 
								 | 
							
								            generated.sqltext, include_table=False, literal_binds=True
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								        if generated.persisted is True:
							 | 
						||
| 
								 | 
							
								            raise exc.CompileError(
							 | 
						||
| 
								 | 
							
								                "Oracle computed columns do not support 'stored' persistence; "
							 | 
						||
| 
								 | 
							
								                "set the 'persisted' flag to None or False for Oracle support."
							 | 
						||
| 
								 | 
							
								            )
							 | 
						||
| 
								 | 
							
								        elif generated.persisted is False:
							 | 
						||
| 
								 | 
							
								            text += " VIRTUAL"
							 | 
						||
| 
								 | 
							
								        return text
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def visit_identity_column(self, identity, **kw):
							 | 
						||
| 
								 | 
							
								        if identity.always is None:
							 | 
						||
| 
								 | 
							
								            kind = ""
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            kind = "ALWAYS" if identity.always else "BY DEFAULT"
							 | 
						||
| 
								 | 
							
								        text = "GENERATED %s" % kind
							 | 
						||
| 
								 | 
							
								        if identity.on_null:
							 | 
						||
| 
								 | 
							
								            text += " ON NULL"
							 | 
						||
| 
								 | 
							
								        text += " AS IDENTITY"
							 | 
						||
| 
								 | 
							
								        options = self.get_identity_options(identity)
							 | 
						||
| 
								 | 
							
								        if options:
							 | 
						||
| 
								 | 
							
								            text += " (%s)" % options
							 | 
						||
| 
								 | 
							
								        return text
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class OracleIdentifierPreparer(compiler.IdentifierPreparer):
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    reserved_words = {x.lower() for x in RESERVED_WORDS}
							 | 
						||
| 
								 | 
							
								    illegal_initial_characters = {str(dig) for dig in range(0, 10)}.union(
							 | 
						||
| 
								 | 
							
								        ["_", "$"]
							 | 
						||
| 
								 | 
							
								    )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def _bindparam_requires_quotes(self, value):
							 | 
						||
| 
								 | 
							
								        """Return True if the given identifier requires quoting."""
							 | 
						||
| 
								 | 
							
								        lc_value = value.lower()
							 | 
						||
| 
								 | 
							
								        return (
							 | 
						||
| 
								 | 
							
								            lc_value in self.reserved_words
							 | 
						||
| 
								 | 
							
								            or value[0] in self.illegal_initial_characters
							 | 
						||
| 
								 | 
							
								            or not self.legal_characters.match(util.text_type(value))
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def format_savepoint(self, savepoint):
							 | 
						||
| 
								 | 
							
								        name = savepoint.ident.lstrip("_")
							 | 
						||
| 
								 | 
							
								        return super(OracleIdentifierPreparer, self).format_savepoint(
							 | 
						||
| 
								 | 
							
								            savepoint, name
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class OracleExecutionContext(default.DefaultExecutionContext):
							 | 
						||
| 
								 | 
							
								    def fire_sequence(self, seq, type_):
							 | 
						||
| 
								 | 
							
								        return self._execute_scalar(
							 | 
						||
| 
								 | 
							
								            "SELECT "
							 | 
						||
| 
								 | 
							
								            + self.identifier_preparer.format_sequence(seq)
							 | 
						||
| 
								 | 
							
								            + ".nextval FROM DUAL",
							 | 
						||
| 
								 | 
							
								            type_,
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class OracleDialect(default.DefaultDialect):
							 | 
						||
| 
								 | 
							
								    name = "oracle"
							 | 
						||
| 
								 | 
							
								    supports_statement_cache = True
							 | 
						||
| 
								 | 
							
								    supports_alter = True
							 | 
						||
| 
								 | 
							
								    supports_unicode_statements = False
							 | 
						||
| 
								 | 
							
								    supports_unicode_binds = False
							 | 
						||
| 
								 | 
							
								    max_identifier_length = 128
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    supports_simple_order_by_label = False
							 | 
						||
| 
								 | 
							
								    cte_follows_insert = True
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    supports_sequences = True
							 | 
						||
| 
								 | 
							
								    sequences_optional = False
							 | 
						||
| 
								 | 
							
								    postfetch_lastrowid = False
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    default_paramstyle = "named"
							 | 
						||
| 
								 | 
							
								    colspecs = colspecs
							 | 
						||
| 
								 | 
							
								    ischema_names = ischema_names
							 | 
						||
| 
								 | 
							
								    requires_name_normalize = True
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    supports_comments = True
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    supports_default_values = False
							 | 
						||
| 
								 | 
							
								    supports_default_metavalue = True
							 | 
						||
| 
								 | 
							
								    supports_empty_insert = False
							 | 
						||
| 
								 | 
							
								    supports_identity_columns = True
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    statement_compiler = OracleCompiler
							 | 
						||
| 
								 | 
							
								    ddl_compiler = OracleDDLCompiler
							 | 
						||
| 
								 | 
							
								    type_compiler = OracleTypeCompiler
							 | 
						||
| 
								 | 
							
								    preparer = OracleIdentifierPreparer
							 | 
						||
| 
								 | 
							
								    execution_ctx_cls = OracleExecutionContext
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    reflection_options = ("oracle_resolve_synonyms",)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    _use_nchar_for_unicode = False
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    construct_arguments = [
							 | 
						||
| 
								 | 
							
								        (
							 | 
						||
| 
								 | 
							
								            sa_schema.Table,
							 | 
						||
| 
								 | 
							
								            {"resolve_synonyms": False, "on_commit": None, "compress": False},
							 | 
						||
| 
								 | 
							
								        ),
							 | 
						||
| 
								 | 
							
								        (sa_schema.Index, {"bitmap": False, "compress": False}),
							 | 
						||
| 
								 | 
							
								    ]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @util.deprecated_params(
							 | 
						||
| 
								 | 
							
								        use_binds_for_limits=(
							 | 
						||
| 
								 | 
							
								            "1.4",
							 | 
						||
| 
								 | 
							
								            "The ``use_binds_for_limits`` Oracle dialect parameter is "
							 | 
						||
| 
								 | 
							
								            "deprecated. The dialect now renders LIMIT /OFFSET integers "
							 | 
						||
| 
								 | 
							
								            "inline in all cases using a post-compilation hook, so that the "
							 | 
						||
| 
								 | 
							
								            "value is still represented by a 'bound parameter' on the Core "
							 | 
						||
| 
								 | 
							
								            "Expression side.",
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								    )
							 | 
						||
| 
								 | 
							
								    def __init__(
							 | 
						||
| 
								 | 
							
								        self,
							 | 
						||
| 
								 | 
							
								        use_ansi=True,
							 | 
						||
| 
								 | 
							
								        optimize_limits=False,
							 | 
						||
| 
								 | 
							
								        use_binds_for_limits=None,
							 | 
						||
| 
								 | 
							
								        use_nchar_for_unicode=False,
							 | 
						||
| 
								 | 
							
								        exclude_tablespaces=("SYSTEM", "SYSAUX"),
							 | 
						||
| 
								 | 
							
								        **kwargs
							 | 
						||
| 
								 | 
							
								    ):
							 | 
						||
| 
								 | 
							
								        default.DefaultDialect.__init__(self, **kwargs)
							 | 
						||
| 
								 | 
							
								        self._use_nchar_for_unicode = use_nchar_for_unicode
							 | 
						||
| 
								 | 
							
								        self.use_ansi = use_ansi
							 | 
						||
| 
								 | 
							
								        self.optimize_limits = optimize_limits
							 | 
						||
| 
								 | 
							
								        self.exclude_tablespaces = exclude_tablespaces
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def initialize(self, connection):
							 | 
						||
| 
								 | 
							
								        super(OracleDialect, self).initialize(connection)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        self.implicit_returning = self.__dict__.get(
							 | 
						||
| 
								 | 
							
								            "implicit_returning", self.server_version_info > (10,)
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if self._is_oracle_8:
							 | 
						||
| 
								 | 
							
								            self.colspecs = self.colspecs.copy()
							 | 
						||
| 
								 | 
							
								            self.colspecs.pop(sqltypes.Interval)
							 | 
						||
| 
								 | 
							
								            self.use_ansi = False
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        self.supports_identity_columns = self.server_version_info >= (12,)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def _get_effective_compat_server_version_info(self, connection):
							 | 
						||
| 
								 | 
							
								        # dialect does not need compat levels below 12.2, so don't query
							 | 
						||
| 
								 | 
							
								        # in those cases
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if self.server_version_info < (12, 2):
							 | 
						||
| 
								 | 
							
								            return self.server_version_info
							 | 
						||
| 
								 | 
							
								        try:
							 | 
						||
| 
								 | 
							
								            compat = connection.exec_driver_sql(
							 | 
						||
| 
								 | 
							
								                "SELECT value FROM v$parameter WHERE name = 'compatible'"
							 | 
						||
| 
								 | 
							
								            ).scalar()
							 | 
						||
| 
								 | 
							
								        except exc.DBAPIError:
							 | 
						||
| 
								 | 
							
								            compat = None
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if compat:
							 | 
						||
| 
								 | 
							
								            try:
							 | 
						||
| 
								 | 
							
								                return tuple(int(x) for x in compat.split("."))
							 | 
						||
| 
								 | 
							
								            except:
							 | 
						||
| 
								 | 
							
								                return self.server_version_info
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            return self.server_version_info
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @property
							 | 
						||
| 
								 | 
							
								    def _is_oracle_8(self):
							 | 
						||
| 
								 | 
							
								        return self.server_version_info and self.server_version_info < (9,)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @property
							 | 
						||
| 
								 | 
							
								    def _supports_table_compression(self):
							 | 
						||
| 
								 | 
							
								        return self.server_version_info and self.server_version_info >= (10, 1)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @property
							 | 
						||
| 
								 | 
							
								    def _supports_table_compress_for(self):
							 | 
						||
| 
								 | 
							
								        return self.server_version_info and self.server_version_info >= (11,)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @property
							 | 
						||
| 
								 | 
							
								    def _supports_char_length(self):
							 | 
						||
| 
								 | 
							
								        return not self._is_oracle_8
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @property
							 | 
						||
| 
								 | 
							
								    def _supports_update_returning_computed_cols(self):
							 | 
						||
| 
								 | 
							
								        # on version 18 this error is no longet present while it happens on 11
							 | 
						||
| 
								 | 
							
								        # it may work also on versions before the 18
							 | 
						||
| 
								 | 
							
								        return self.server_version_info and self.server_version_info >= (18,)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def do_release_savepoint(self, connection, name):
							 | 
						||
| 
								 | 
							
								        # Oracle does not support RELEASE SAVEPOINT
							 | 
						||
| 
								 | 
							
								        pass
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def _check_max_identifier_length(self, connection):
							 | 
						||
| 
								 | 
							
								        if self._get_effective_compat_server_version_info(connection) < (
							 | 
						||
| 
								 | 
							
								            12,
							 | 
						||
| 
								 | 
							
								            2,
							 | 
						||
| 
								 | 
							
								        ):
							 | 
						||
| 
								 | 
							
								            return 30
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            # use the default
							 | 
						||
| 
								 | 
							
								            return None
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def _check_unicode_returns(self, connection):
							 | 
						||
| 
								 | 
							
								        additional_tests = [
							 | 
						||
| 
								 | 
							
								            expression.cast(
							 | 
						||
| 
								 | 
							
								                expression.literal_column("'test nvarchar2 returns'"),
							 | 
						||
| 
								 | 
							
								                sqltypes.NVARCHAR(60),
							 | 
						||
| 
								 | 
							
								            )
							 | 
						||
| 
								 | 
							
								        ]
							 | 
						||
| 
								 | 
							
								        return super(OracleDialect, self)._check_unicode_returns(
							 | 
						||
| 
								 | 
							
								            connection, additional_tests
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    _isolation_lookup = ["READ COMMITTED", "SERIALIZABLE"]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def get_isolation_level(self, connection):
							 | 
						||
| 
								 | 
							
								        raise NotImplementedError("implemented by cx_Oracle dialect")
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def get_default_isolation_level(self, dbapi_conn):
							 | 
						||
| 
								 | 
							
								        try:
							 | 
						||
| 
								 | 
							
								            return self.get_isolation_level(dbapi_conn)
							 | 
						||
| 
								 | 
							
								        except NotImplementedError:
							 | 
						||
| 
								 | 
							
								            raise
							 | 
						||
| 
								 | 
							
								        except:
							 | 
						||
| 
								 | 
							
								            return "READ COMMITTED"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def set_isolation_level(self, connection, level):
							 | 
						||
| 
								 | 
							
								        raise NotImplementedError("implemented by cx_Oracle dialect")
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def has_table(self, connection, table_name, schema=None):
							 | 
						||
| 
								 | 
							
								        self._ensure_has_table_connection(connection)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if not schema:
							 | 
						||
| 
								 | 
							
								            schema = self.default_schema_name
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        cursor = connection.execute(
							 | 
						||
| 
								 | 
							
								            sql.text(
							 | 
						||
| 
								 | 
							
								                "SELECT table_name FROM all_tables "
							 | 
						||
| 
								 | 
							
								                "WHERE table_name = CAST(:name AS VARCHAR2(128)) "
							 | 
						||
| 
								 | 
							
								                "AND owner = CAST(:schema_name AS VARCHAR2(128))"
							 | 
						||
| 
								 | 
							
								            ),
							 | 
						||
| 
								 | 
							
								            dict(
							 | 
						||
| 
								 | 
							
								                name=self.denormalize_name(table_name),
							 | 
						||
| 
								 | 
							
								                schema_name=self.denormalize_name(schema),
							 | 
						||
| 
								 | 
							
								            ),
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								        return cursor.first() is not None
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def has_sequence(self, connection, sequence_name, schema=None):
							 | 
						||
| 
								 | 
							
								        if not schema:
							 | 
						||
| 
								 | 
							
								            schema = self.default_schema_name
							 | 
						||
| 
								 | 
							
								        cursor = connection.execute(
							 | 
						||
| 
								 | 
							
								            sql.text(
							 | 
						||
| 
								 | 
							
								                "SELECT sequence_name FROM all_sequences "
							 | 
						||
| 
								 | 
							
								                "WHERE sequence_name = :name AND "
							 | 
						||
| 
								 | 
							
								                "sequence_owner = :schema_name"
							 | 
						||
| 
								 | 
							
								            ),
							 | 
						||
| 
								 | 
							
								            dict(
							 | 
						||
| 
								 | 
							
								                name=self.denormalize_name(sequence_name),
							 | 
						||
| 
								 | 
							
								                schema_name=self.denormalize_name(schema),
							 | 
						||
| 
								 | 
							
								            ),
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								        return cursor.first() is not None
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def _get_default_schema_name(self, connection):
							 | 
						||
| 
								 | 
							
								        return self.normalize_name(
							 | 
						||
| 
								 | 
							
								            connection.exec_driver_sql(
							 | 
						||
| 
								 | 
							
								                "select sys_context( 'userenv', 'current_schema' ) from dual"
							 | 
						||
| 
								 | 
							
								            ).scalar()
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def _resolve_synonym(
							 | 
						||
| 
								 | 
							
								        self,
							 | 
						||
| 
								 | 
							
								        connection,
							 | 
						||
| 
								 | 
							
								        desired_owner=None,
							 | 
						||
| 
								 | 
							
								        desired_synonym=None,
							 | 
						||
| 
								 | 
							
								        desired_table=None,
							 | 
						||
| 
								 | 
							
								    ):
							 | 
						||
| 
								 | 
							
								        """search for a local synonym matching the given desired owner/name.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if desired_owner is None, attempts to locate a distinct owner.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        returns the actual name, owner, dblink name, and synonym name if
							 | 
						||
| 
								 | 
							
								        found.
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        q = (
							 | 
						||
| 
								 | 
							
								            "SELECT owner, table_owner, table_name, db_link, "
							 | 
						||
| 
								 | 
							
								            "synonym_name FROM all_synonyms WHERE "
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								        clauses = []
							 | 
						||
| 
								 | 
							
								        params = {}
							 | 
						||
| 
								 | 
							
								        if desired_synonym:
							 | 
						||
| 
								 | 
							
								            clauses.append(
							 | 
						||
| 
								 | 
							
								                "synonym_name = CAST(:synonym_name AS VARCHAR2(128))"
							 | 
						||
| 
								 | 
							
								            )
							 | 
						||
| 
								 | 
							
								            params["synonym_name"] = desired_synonym
							 | 
						||
| 
								 | 
							
								        if desired_owner:
							 | 
						||
| 
								 | 
							
								            clauses.append("owner = CAST(:desired_owner AS VARCHAR2(128))")
							 | 
						||
| 
								 | 
							
								            params["desired_owner"] = desired_owner
							 | 
						||
| 
								 | 
							
								        if desired_table:
							 | 
						||
| 
								 | 
							
								            clauses.append("table_name = CAST(:tname AS VARCHAR2(128))")
							 | 
						||
| 
								 | 
							
								            params["tname"] = desired_table
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        q += " AND ".join(clauses)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        result = connection.execution_options(future_result=True).execute(
							 | 
						||
| 
								 | 
							
								            sql.text(q), params
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								        if desired_owner:
							 | 
						||
| 
								 | 
							
								            row = result.mappings().first()
							 | 
						||
| 
								 | 
							
								            if row:
							 | 
						||
| 
								 | 
							
								                return (
							 | 
						||
| 
								 | 
							
								                    row["table_name"],
							 | 
						||
| 
								 | 
							
								                    row["table_owner"],
							 | 
						||
| 
								 | 
							
								                    row["db_link"],
							 | 
						||
| 
								 | 
							
								                    row["synonym_name"],
							 | 
						||
| 
								 | 
							
								                )
							 | 
						||
| 
								 | 
							
								            else:
							 | 
						||
| 
								 | 
							
								                return None, None, None, None
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            rows = result.mappings().all()
							 | 
						||
| 
								 | 
							
								            if len(rows) > 1:
							 | 
						||
| 
								 | 
							
								                raise AssertionError(
							 | 
						||
| 
								 | 
							
								                    "There are multiple tables visible to the schema, you "
							 | 
						||
| 
								 | 
							
								                    "must specify owner"
							 | 
						||
| 
								 | 
							
								                )
							 | 
						||
| 
								 | 
							
								            elif len(rows) == 1:
							 | 
						||
| 
								 | 
							
								                row = rows[0]
							 | 
						||
| 
								 | 
							
								                return (
							 | 
						||
| 
								 | 
							
								                    row["table_name"],
							 | 
						||
| 
								 | 
							
								                    row["table_owner"],
							 | 
						||
| 
								 | 
							
								                    row["db_link"],
							 | 
						||
| 
								 | 
							
								                    row["synonym_name"],
							 | 
						||
| 
								 | 
							
								                )
							 | 
						||
| 
								 | 
							
								            else:
							 | 
						||
| 
								 | 
							
								                return None, None, None, None
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @reflection.cache
							 | 
						||
| 
								 | 
							
								    def _prepare_reflection_args(
							 | 
						||
| 
								 | 
							
								        self,
							 | 
						||
| 
								 | 
							
								        connection,
							 | 
						||
| 
								 | 
							
								        table_name,
							 | 
						||
| 
								 | 
							
								        schema=None,
							 | 
						||
| 
								 | 
							
								        resolve_synonyms=False,
							 | 
						||
| 
								 | 
							
								        dblink="",
							 | 
						||
| 
								 | 
							
								        **kw
							 | 
						||
| 
								 | 
							
								    ):
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if resolve_synonyms:
							 | 
						||
| 
								 | 
							
								            actual_name, owner, dblink, synonym = self._resolve_synonym(
							 | 
						||
| 
								 | 
							
								                connection,
							 | 
						||
| 
								 | 
							
								                desired_owner=self.denormalize_name(schema),
							 | 
						||
| 
								 | 
							
								                desired_synonym=self.denormalize_name(table_name),
							 | 
						||
| 
								 | 
							
								            )
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            actual_name, owner, dblink, synonym = None, None, None, None
							 | 
						||
| 
								 | 
							
								        if not actual_name:
							 | 
						||
| 
								 | 
							
								            actual_name = self.denormalize_name(table_name)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if dblink:
							 | 
						||
| 
								 | 
							
								            # using user_db_links here since all_db_links appears
							 | 
						||
| 
								 | 
							
								            # to have more restricted permissions.
							 | 
						||
| 
								 | 
							
								            # https://docs.oracle.com/cd/B28359_01/server.111/b28310/ds_admin005.htm
							 | 
						||
| 
								 | 
							
								            # will need to hear from more users if we are doing
							 | 
						||
| 
								 | 
							
								            # the right thing here.  See [ticket:2619]
							 | 
						||
| 
								 | 
							
								            owner = connection.scalar(
							 | 
						||
| 
								 | 
							
								                sql.text(
							 | 
						||
| 
								 | 
							
								                    "SELECT username FROM user_db_links " "WHERE db_link=:link"
							 | 
						||
| 
								 | 
							
								                ),
							 | 
						||
| 
								 | 
							
								                dict(link=dblink),
							 | 
						||
| 
								 | 
							
								            )
							 | 
						||
| 
								 | 
							
								            dblink = "@" + dblink
							 | 
						||
| 
								 | 
							
								        elif not owner:
							 | 
						||
| 
								 | 
							
								            owner = self.denormalize_name(schema or self.default_schema_name)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        return (actual_name, owner, dblink or "", synonym)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @reflection.cache
							 | 
						||
| 
								 | 
							
								    def get_schema_names(self, connection, **kw):
							 | 
						||
| 
								 | 
							
								        s = "SELECT username FROM all_users ORDER BY username"
							 | 
						||
| 
								 | 
							
								        cursor = connection.exec_driver_sql(s)
							 | 
						||
| 
								 | 
							
								        return [self.normalize_name(row[0]) for row in cursor]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @reflection.cache
							 | 
						||
| 
								 | 
							
								    def get_table_names(self, connection, schema=None, **kw):
							 | 
						||
| 
								 | 
							
								        schema = self.denormalize_name(schema or self.default_schema_name)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # note that table_names() isn't loading DBLINKed or synonym'ed tables
							 | 
						||
| 
								 | 
							
								        if schema is None:
							 | 
						||
| 
								 | 
							
								            schema = self.default_schema_name
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        sql_str = "SELECT table_name FROM all_tables WHERE "
							 | 
						||
| 
								 | 
							
								        if self.exclude_tablespaces:
							 | 
						||
| 
								 | 
							
								            sql_str += (
							 | 
						||
| 
								 | 
							
								                "nvl(tablespace_name, 'no tablespace') "
							 | 
						||
| 
								 | 
							
								                "NOT IN (%s) AND "
							 | 
						||
| 
								 | 
							
								                % (", ".join(["'%s'" % ts for ts in self.exclude_tablespaces]))
							 | 
						||
| 
								 | 
							
								            )
							 | 
						||
| 
								 | 
							
								        sql_str += (
							 | 
						||
| 
								 | 
							
								            "OWNER = :owner " "AND IOT_NAME IS NULL " "AND DURATION IS NULL"
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        cursor = connection.execute(sql.text(sql_str), dict(owner=schema))
							 | 
						||
| 
								 | 
							
								        return [self.normalize_name(row[0]) for row in cursor]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @reflection.cache
							 | 
						||
| 
								 | 
							
								    def get_temp_table_names(self, connection, **kw):
							 | 
						||
| 
								 | 
							
								        schema = self.denormalize_name(self.default_schema_name)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        sql_str = "SELECT table_name FROM all_tables WHERE "
							 | 
						||
| 
								 | 
							
								        if self.exclude_tablespaces:
							 | 
						||
| 
								 | 
							
								            sql_str += (
							 | 
						||
| 
								 | 
							
								                "nvl(tablespace_name, 'no tablespace') "
							 | 
						||
| 
								 | 
							
								                "NOT IN (%s) AND "
							 | 
						||
| 
								 | 
							
								                % (", ".join(["'%s'" % ts for ts in self.exclude_tablespaces]))
							 | 
						||
| 
								 | 
							
								            )
							 | 
						||
| 
								 | 
							
								        sql_str += (
							 | 
						||
| 
								 | 
							
								            "OWNER = :owner "
							 | 
						||
| 
								 | 
							
								            "AND IOT_NAME IS NULL "
							 | 
						||
| 
								 | 
							
								            "AND DURATION IS NOT NULL"
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        cursor = connection.execute(sql.text(sql_str), dict(owner=schema))
							 | 
						||
| 
								 | 
							
								        return [self.normalize_name(row[0]) for row in cursor]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @reflection.cache
							 | 
						||
| 
								 | 
							
								    def get_view_names(self, connection, schema=None, **kw):
							 | 
						||
| 
								 | 
							
								        schema = self.denormalize_name(schema or self.default_schema_name)
							 | 
						||
| 
								 | 
							
								        s = sql.text("SELECT view_name FROM all_views WHERE owner = :owner")
							 | 
						||
| 
								 | 
							
								        cursor = connection.execute(
							 | 
						||
| 
								 | 
							
								            s, dict(owner=self.denormalize_name(schema))
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								        return [self.normalize_name(row[0]) for row in cursor]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @reflection.cache
							 | 
						||
| 
								 | 
							
								    def get_sequence_names(self, connection, schema=None, **kw):
							 | 
						||
| 
								 | 
							
								        if not schema:
							 | 
						||
| 
								 | 
							
								            schema = self.default_schema_name
							 | 
						||
| 
								 | 
							
								        cursor = connection.execute(
							 | 
						||
| 
								 | 
							
								            sql.text(
							 | 
						||
| 
								 | 
							
								                "SELECT sequence_name FROM all_sequences "
							 | 
						||
| 
								 | 
							
								                "WHERE sequence_owner = :schema_name"
							 | 
						||
| 
								 | 
							
								            ),
							 | 
						||
| 
								 | 
							
								            dict(schema_name=self.denormalize_name(schema)),
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								        return [self.normalize_name(row[0]) for row in cursor]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @reflection.cache
							 | 
						||
| 
								 | 
							
								    def get_table_options(self, connection, table_name, schema=None, **kw):
							 | 
						||
| 
								 | 
							
								        options = {}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        resolve_synonyms = kw.get("oracle_resolve_synonyms", False)
							 | 
						||
| 
								 | 
							
								        dblink = kw.get("dblink", "")
							 | 
						||
| 
								 | 
							
								        info_cache = kw.get("info_cache")
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        (table_name, schema, dblink, synonym) = self._prepare_reflection_args(
							 | 
						||
| 
								 | 
							
								            connection,
							 | 
						||
| 
								 | 
							
								            table_name,
							 | 
						||
| 
								 | 
							
								            schema,
							 | 
						||
| 
								 | 
							
								            resolve_synonyms,
							 | 
						||
| 
								 | 
							
								            dblink,
							 | 
						||
| 
								 | 
							
								            info_cache=info_cache,
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        params = {"table_name": table_name}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        columns = ["table_name"]
							 | 
						||
| 
								 | 
							
								        if self._supports_table_compression:
							 | 
						||
| 
								 | 
							
								            columns.append("compression")
							 | 
						||
| 
								 | 
							
								        if self._supports_table_compress_for:
							 | 
						||
| 
								 | 
							
								            columns.append("compress_for")
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        text = (
							 | 
						||
| 
								 | 
							
								            "SELECT %(columns)s "
							 | 
						||
| 
								 | 
							
								            "FROM ALL_TABLES%(dblink)s "
							 | 
						||
| 
								 | 
							
								            "WHERE table_name = CAST(:table_name AS VARCHAR(128))"
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if schema is not None:
							 | 
						||
| 
								 | 
							
								            params["owner"] = schema
							 | 
						||
| 
								 | 
							
								            text += " AND owner = CAST(:owner AS VARCHAR(128)) "
							 | 
						||
| 
								 | 
							
								        text = text % {"dblink": dblink, "columns": ", ".join(columns)}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        result = connection.execute(sql.text(text), params)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        enabled = dict(DISABLED=False, ENABLED=True)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        row = result.first()
							 | 
						||
| 
								 | 
							
								        if row:
							 | 
						||
| 
								 | 
							
								            if "compression" in row._fields and enabled.get(
							 | 
						||
| 
								 | 
							
								                row.compression, False
							 | 
						||
| 
								 | 
							
								            ):
							 | 
						||
| 
								 | 
							
								                if "compress_for" in row._fields:
							 | 
						||
| 
								 | 
							
								                    options["oracle_compress"] = row.compress_for
							 | 
						||
| 
								 | 
							
								                else:
							 | 
						||
| 
								 | 
							
								                    options["oracle_compress"] = True
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        return options
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @reflection.cache
							 | 
						||
| 
								 | 
							
								    def get_columns(self, connection, table_name, schema=None, **kw):
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        kw arguments can be:
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            oracle_resolve_synonyms
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            dblink
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        resolve_synonyms = kw.get("oracle_resolve_synonyms", False)
							 | 
						||
| 
								 | 
							
								        dblink = kw.get("dblink", "")
							 | 
						||
| 
								 | 
							
								        info_cache = kw.get("info_cache")
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        (table_name, schema, dblink, synonym) = self._prepare_reflection_args(
							 | 
						||
| 
								 | 
							
								            connection,
							 | 
						||
| 
								 | 
							
								            table_name,
							 | 
						||
| 
								 | 
							
								            schema,
							 | 
						||
| 
								 | 
							
								            resolve_synonyms,
							 | 
						||
| 
								 | 
							
								            dblink,
							 | 
						||
| 
								 | 
							
								            info_cache=info_cache,
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								        columns = []
							 | 
						||
| 
								 | 
							
								        if self._supports_char_length:
							 | 
						||
| 
								 | 
							
								            char_length_col = "char_length"
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            char_length_col = "data_length"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if self.server_version_info >= (12,):
							 | 
						||
| 
								 | 
							
								            identity_cols = """\
							 | 
						||
| 
								 | 
							
								                col.default_on_null,
							 | 
						||
| 
								 | 
							
								                (
							 | 
						||
| 
								 | 
							
								                    SELECT id.generation_type || ',' || id.IDENTITY_OPTIONS
							 | 
						||
| 
								 | 
							
								                    FROM ALL_TAB_IDENTITY_COLS%(dblink)s id
							 | 
						||
| 
								 | 
							
								                    WHERE col.table_name = id.table_name
							 | 
						||
| 
								 | 
							
								                    AND col.column_name = id.column_name
							 | 
						||
| 
								 | 
							
								                    AND col.owner = id.owner
							 | 
						||
| 
								 | 
							
								                ) AS identity_options""" % {
							 | 
						||
| 
								 | 
							
								                "dblink": dblink
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            identity_cols = "NULL as default_on_null, NULL as identity_options"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        params = {"table_name": table_name}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        text = """
							 | 
						||
| 
								 | 
							
								            SELECT
							 | 
						||
| 
								 | 
							
								                col.column_name,
							 | 
						||
| 
								 | 
							
								                col.data_type,
							 | 
						||
| 
								 | 
							
								                col.%(char_length_col)s,
							 | 
						||
| 
								 | 
							
								                col.data_precision,
							 | 
						||
| 
								 | 
							
								                col.data_scale,
							 | 
						||
| 
								 | 
							
								                col.nullable,
							 | 
						||
| 
								 | 
							
								                col.data_default,
							 | 
						||
| 
								 | 
							
								                com.comments,
							 | 
						||
| 
								 | 
							
								                col.virtual_column,
							 | 
						||
| 
								 | 
							
								                %(identity_cols)s
							 | 
						||
| 
								 | 
							
								            FROM all_tab_cols%(dblink)s col
							 | 
						||
| 
								 | 
							
								            LEFT JOIN all_col_comments%(dblink)s com
							 | 
						||
| 
								 | 
							
								            ON col.table_name = com.table_name
							 | 
						||
| 
								 | 
							
								            AND col.column_name = com.column_name
							 | 
						||
| 
								 | 
							
								            AND col.owner = com.owner
							 | 
						||
| 
								 | 
							
								            WHERE col.table_name = CAST(:table_name AS VARCHAR2(128))
							 | 
						||
| 
								 | 
							
								            AND col.hidden_column = 'NO'
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        if schema is not None:
							 | 
						||
| 
								 | 
							
								            params["owner"] = schema
							 | 
						||
| 
								 | 
							
								            text += " AND col.owner = :owner "
							 | 
						||
| 
								 | 
							
								        text += " ORDER BY col.column_id"
							 | 
						||
| 
								 | 
							
								        text = text % {
							 | 
						||
| 
								 | 
							
								            "dblink": dblink,
							 | 
						||
| 
								 | 
							
								            "char_length_col": char_length_col,
							 | 
						||
| 
								 | 
							
								            "identity_cols": identity_cols,
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        c = connection.execute(sql.text(text), params)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        for row in c:
							 | 
						||
| 
								 | 
							
								            colname = self.normalize_name(row[0])
							 | 
						||
| 
								 | 
							
								            orig_colname = row[0]
							 | 
						||
| 
								 | 
							
								            coltype = row[1]
							 | 
						||
| 
								 | 
							
								            length = row[2]
							 | 
						||
| 
								 | 
							
								            precision = row[3]
							 | 
						||
| 
								 | 
							
								            scale = row[4]
							 | 
						||
| 
								 | 
							
								            nullable = row[5] == "Y"
							 | 
						||
| 
								 | 
							
								            default = row[6]
							 | 
						||
| 
								 | 
							
								            comment = row[7]
							 | 
						||
| 
								 | 
							
								            generated = row[8]
							 | 
						||
| 
								 | 
							
								            default_on_nul = row[9]
							 | 
						||
| 
								 | 
							
								            identity_options = row[10]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            if coltype == "NUMBER":
							 | 
						||
| 
								 | 
							
								                if precision is None and scale == 0:
							 | 
						||
| 
								 | 
							
								                    coltype = INTEGER()
							 | 
						||
| 
								 | 
							
								                else:
							 | 
						||
| 
								 | 
							
								                    coltype = NUMBER(precision, scale)
							 | 
						||
| 
								 | 
							
								            elif coltype == "FLOAT":
							 | 
						||
| 
								 | 
							
								                # TODO: support "precision" here as "binary_precision"
							 | 
						||
| 
								 | 
							
								                coltype = FLOAT()
							 | 
						||
| 
								 | 
							
								            elif coltype in ("VARCHAR2", "NVARCHAR2", "CHAR", "NCHAR"):
							 | 
						||
| 
								 | 
							
								                coltype = self.ischema_names.get(coltype)(length)
							 | 
						||
| 
								 | 
							
								            elif "WITH TIME ZONE" in coltype:
							 | 
						||
| 
								 | 
							
								                coltype = TIMESTAMP(timezone=True)
							 | 
						||
| 
								 | 
							
								            else:
							 | 
						||
| 
								 | 
							
								                coltype = re.sub(r"\(\d+\)", "", coltype)
							 | 
						||
| 
								 | 
							
								                try:
							 | 
						||
| 
								 | 
							
								                    coltype = self.ischema_names[coltype]
							 | 
						||
| 
								 | 
							
								                except KeyError:
							 | 
						||
| 
								 | 
							
								                    util.warn(
							 | 
						||
| 
								 | 
							
								                        "Did not recognize type '%s' of column '%s'"
							 | 
						||
| 
								 | 
							
								                        % (coltype, colname)
							 | 
						||
| 
								 | 
							
								                    )
							 | 
						||
| 
								 | 
							
								                    coltype = sqltypes.NULLTYPE
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            if generated == "YES":
							 | 
						||
| 
								 | 
							
								                computed = dict(sqltext=default)
							 | 
						||
| 
								 | 
							
								                default = None
							 | 
						||
| 
								 | 
							
								            else:
							 | 
						||
| 
								 | 
							
								                computed = None
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            if identity_options is not None:
							 | 
						||
| 
								 | 
							
								                identity = self._parse_identity_options(
							 | 
						||
| 
								 | 
							
								                    identity_options, default_on_nul
							 | 
						||
| 
								 | 
							
								                )
							 | 
						||
| 
								 | 
							
								                default = None
							 | 
						||
| 
								 | 
							
								            else:
							 | 
						||
| 
								 | 
							
								                identity = None
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            cdict = {
							 | 
						||
| 
								 | 
							
								                "name": colname,
							 | 
						||
| 
								 | 
							
								                "type": coltype,
							 | 
						||
| 
								 | 
							
								                "nullable": nullable,
							 | 
						||
| 
								 | 
							
								                "default": default,
							 | 
						||
| 
								 | 
							
								                "autoincrement": "auto",
							 | 
						||
| 
								 | 
							
								                "comment": comment,
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            if orig_colname.lower() == orig_colname:
							 | 
						||
| 
								 | 
							
								                cdict["quote"] = True
							 | 
						||
| 
								 | 
							
								            if computed is not None:
							 | 
						||
| 
								 | 
							
								                cdict["computed"] = computed
							 | 
						||
| 
								 | 
							
								            if identity is not None:
							 | 
						||
| 
								 | 
							
								                cdict["identity"] = identity
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            columns.append(cdict)
							 | 
						||
| 
								 | 
							
								        return columns
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def _parse_identity_options(self, identity_options, default_on_nul):
							 | 
						||
| 
								 | 
							
								        # identity_options is a string that starts with 'ALWAYS,' or
							 | 
						||
| 
								 | 
							
								        # 'BY DEFAULT,' and continues with
							 | 
						||
| 
								 | 
							
								        # START WITH: 1, INCREMENT BY: 1, MAX_VALUE: 123, MIN_VALUE: 1,
							 | 
						||
| 
								 | 
							
								        # CYCLE_FLAG: N, CACHE_SIZE: 1, ORDER_FLAG: N, SCALE_FLAG: N,
							 | 
						||
| 
								 | 
							
								        # EXTEND_FLAG: N, SESSION_FLAG: N, KEEP_VALUE: N
							 | 
						||
| 
								 | 
							
								        parts = [p.strip() for p in identity_options.split(",")]
							 | 
						||
| 
								 | 
							
								        identity = {
							 | 
						||
| 
								 | 
							
								            "always": parts[0] == "ALWAYS",
							 | 
						||
| 
								 | 
							
								            "on_null": default_on_nul == "YES",
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        for part in parts[1:]:
							 | 
						||
| 
								 | 
							
								            option, value = part.split(":")
							 | 
						||
| 
								 | 
							
								            value = value.strip()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            if "START WITH" in option:
							 | 
						||
| 
								 | 
							
								                identity["start"] = compat.long_type(value)
							 | 
						||
| 
								 | 
							
								            elif "INCREMENT BY" in option:
							 | 
						||
| 
								 | 
							
								                identity["increment"] = compat.long_type(value)
							 | 
						||
| 
								 | 
							
								            elif "MAX_VALUE" in option:
							 | 
						||
| 
								 | 
							
								                identity["maxvalue"] = compat.long_type(value)
							 | 
						||
| 
								 | 
							
								            elif "MIN_VALUE" in option:
							 | 
						||
| 
								 | 
							
								                identity["minvalue"] = compat.long_type(value)
							 | 
						||
| 
								 | 
							
								            elif "CYCLE_FLAG" in option:
							 | 
						||
| 
								 | 
							
								                identity["cycle"] = value == "Y"
							 | 
						||
| 
								 | 
							
								            elif "CACHE_SIZE" in option:
							 | 
						||
| 
								 | 
							
								                identity["cache"] = compat.long_type(value)
							 | 
						||
| 
								 | 
							
								            elif "ORDER_FLAG" in option:
							 | 
						||
| 
								 | 
							
								                identity["order"] = value == "Y"
							 | 
						||
| 
								 | 
							
								        return identity
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @reflection.cache
							 | 
						||
| 
								 | 
							
								    def get_table_comment(
							 | 
						||
| 
								 | 
							
								        self,
							 | 
						||
| 
								 | 
							
								        connection,
							 | 
						||
| 
								 | 
							
								        table_name,
							 | 
						||
| 
								 | 
							
								        schema=None,
							 | 
						||
| 
								 | 
							
								        resolve_synonyms=False,
							 | 
						||
| 
								 | 
							
								        dblink="",
							 | 
						||
| 
								 | 
							
								        **kw
							 | 
						||
| 
								 | 
							
								    ):
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        info_cache = kw.get("info_cache")
							 | 
						||
| 
								 | 
							
								        (table_name, schema, dblink, synonym) = self._prepare_reflection_args(
							 | 
						||
| 
								 | 
							
								            connection,
							 | 
						||
| 
								 | 
							
								            table_name,
							 | 
						||
| 
								 | 
							
								            schema,
							 | 
						||
| 
								 | 
							
								            resolve_synonyms,
							 | 
						||
| 
								 | 
							
								            dblink,
							 | 
						||
| 
								 | 
							
								            info_cache=info_cache,
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if not schema:
							 | 
						||
| 
								 | 
							
								            schema = self.default_schema_name
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        COMMENT_SQL = """
							 | 
						||
| 
								 | 
							
								            SELECT comments
							 | 
						||
| 
								 | 
							
								            FROM all_tab_comments
							 | 
						||
| 
								 | 
							
								            WHERE table_name = CAST(:table_name AS VARCHAR(128))
							 | 
						||
| 
								 | 
							
								            AND owner = CAST(:schema_name AS VARCHAR(128))
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        c = connection.execute(
							 | 
						||
| 
								 | 
							
								            sql.text(COMMENT_SQL),
							 | 
						||
| 
								 | 
							
								            dict(table_name=table_name, schema_name=schema),
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								        return {"text": c.scalar()}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @reflection.cache
							 | 
						||
| 
								 | 
							
								    def get_indexes(
							 | 
						||
| 
								 | 
							
								        self,
							 | 
						||
| 
								 | 
							
								        connection,
							 | 
						||
| 
								 | 
							
								        table_name,
							 | 
						||
| 
								 | 
							
								        schema=None,
							 | 
						||
| 
								 | 
							
								        resolve_synonyms=False,
							 | 
						||
| 
								 | 
							
								        dblink="",
							 | 
						||
| 
								 | 
							
								        **kw
							 | 
						||
| 
								 | 
							
								    ):
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        info_cache = kw.get("info_cache")
							 | 
						||
| 
								 | 
							
								        (table_name, schema, dblink, synonym) = self._prepare_reflection_args(
							 | 
						||
| 
								 | 
							
								            connection,
							 | 
						||
| 
								 | 
							
								            table_name,
							 | 
						||
| 
								 | 
							
								            schema,
							 | 
						||
| 
								 | 
							
								            resolve_synonyms,
							 | 
						||
| 
								 | 
							
								            dblink,
							 | 
						||
| 
								 | 
							
								            info_cache=info_cache,
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								        indexes = []
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        params = {"table_name": table_name}
							 | 
						||
| 
								 | 
							
								        text = (
							 | 
						||
| 
								 | 
							
								            "SELECT a.index_name, a.column_name, "
							 | 
						||
| 
								 | 
							
								            "\nb.index_type, b.uniqueness, b.compression, b.prefix_length "
							 | 
						||
| 
								 | 
							
								            "\nFROM ALL_IND_COLUMNS%(dblink)s a, "
							 | 
						||
| 
								 | 
							
								            "\nALL_INDEXES%(dblink)s b "
							 | 
						||
| 
								 | 
							
								            "\nWHERE "
							 | 
						||
| 
								 | 
							
								            "\na.index_name = b.index_name "
							 | 
						||
| 
								 | 
							
								            "\nAND a.table_owner = b.table_owner "
							 | 
						||
| 
								 | 
							
								            "\nAND a.table_name = b.table_name "
							 | 
						||
| 
								 | 
							
								            "\nAND a.table_name = CAST(:table_name AS VARCHAR(128))"
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if schema is not None:
							 | 
						||
| 
								 | 
							
								            params["schema"] = schema
							 | 
						||
| 
								 | 
							
								            text += "AND a.table_owner = :schema "
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        text += "ORDER BY a.index_name, a.column_position"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        text = text % {"dblink": dblink}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        q = sql.text(text)
							 | 
						||
| 
								 | 
							
								        rp = connection.execute(q, params)
							 | 
						||
| 
								 | 
							
								        indexes = []
							 | 
						||
| 
								 | 
							
								        last_index_name = None
							 | 
						||
| 
								 | 
							
								        pk_constraint = self.get_pk_constraint(
							 | 
						||
| 
								 | 
							
								            connection,
							 | 
						||
| 
								 | 
							
								            table_name,
							 | 
						||
| 
								 | 
							
								            schema,
							 | 
						||
| 
								 | 
							
								            resolve_synonyms=resolve_synonyms,
							 | 
						||
| 
								 | 
							
								            dblink=dblink,
							 | 
						||
| 
								 | 
							
								            info_cache=kw.get("info_cache"),
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        uniqueness = dict(NONUNIQUE=False, UNIQUE=True)
							 | 
						||
| 
								 | 
							
								        enabled = dict(DISABLED=False, ENABLED=True)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        oracle_sys_col = re.compile(r"SYS_NC\d+\$", re.IGNORECASE)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        index = None
							 | 
						||
| 
								 | 
							
								        for rset in rp:
							 | 
						||
| 
								 | 
							
								            index_name_normalized = self.normalize_name(rset.index_name)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            # skip primary key index.  This is refined as of
							 | 
						||
| 
								 | 
							
								            # [ticket:5421].  Note that ALL_INDEXES.GENERATED will by "Y"
							 | 
						||
| 
								 | 
							
								            # if the name of this index was generated by Oracle, however
							 | 
						||
| 
								 | 
							
								            # if a named primary key constraint was created then this flag
							 | 
						||
| 
								 | 
							
								            # is false.
							 | 
						||
| 
								 | 
							
								            if (
							 | 
						||
| 
								 | 
							
								                pk_constraint
							 | 
						||
| 
								 | 
							
								                and index_name_normalized == pk_constraint["name"]
							 | 
						||
| 
								 | 
							
								            ):
							 | 
						||
| 
								 | 
							
								                continue
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            if rset.index_name != last_index_name:
							 | 
						||
| 
								 | 
							
								                index = dict(
							 | 
						||
| 
								 | 
							
								                    name=index_name_normalized,
							 | 
						||
| 
								 | 
							
								                    column_names=[],
							 | 
						||
| 
								 | 
							
								                    dialect_options={},
							 | 
						||
| 
								 | 
							
								                )
							 | 
						||
| 
								 | 
							
								                indexes.append(index)
							 | 
						||
| 
								 | 
							
								            index["unique"] = uniqueness.get(rset.uniqueness, False)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            if rset.index_type in ("BITMAP", "FUNCTION-BASED BITMAP"):
							 | 
						||
| 
								 | 
							
								                index["dialect_options"]["oracle_bitmap"] = True
							 | 
						||
| 
								 | 
							
								            if enabled.get(rset.compression, False):
							 | 
						||
| 
								 | 
							
								                index["dialect_options"][
							 | 
						||
| 
								 | 
							
								                    "oracle_compress"
							 | 
						||
| 
								 | 
							
								                ] = rset.prefix_length
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            # filter out Oracle SYS_NC names.  could also do an outer join
							 | 
						||
| 
								 | 
							
								            # to the all_tab_columns table and check for real col names there.
							 | 
						||
| 
								 | 
							
								            if not oracle_sys_col.match(rset.column_name):
							 | 
						||
| 
								 | 
							
								                index["column_names"].append(
							 | 
						||
| 
								 | 
							
								                    self.normalize_name(rset.column_name)
							 | 
						||
| 
								 | 
							
								                )
							 | 
						||
| 
								 | 
							
								            last_index_name = rset.index_name
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        return indexes
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @reflection.cache
							 | 
						||
| 
								 | 
							
								    def _get_constraint_data(
							 | 
						||
| 
								 | 
							
								        self, connection, table_name, schema=None, dblink="", **kw
							 | 
						||
| 
								 | 
							
								    ):
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        params = {"table_name": table_name}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        text = (
							 | 
						||
| 
								 | 
							
								            "SELECT"
							 | 
						||
| 
								 | 
							
								            "\nac.constraint_name,"  # 0
							 | 
						||
| 
								 | 
							
								            "\nac.constraint_type,"  # 1
							 | 
						||
| 
								 | 
							
								            "\nloc.column_name AS local_column,"  # 2
							 | 
						||
| 
								 | 
							
								            "\nrem.table_name AS remote_table,"  # 3
							 | 
						||
| 
								 | 
							
								            "\nrem.column_name AS remote_column,"  # 4
							 | 
						||
| 
								 | 
							
								            "\nrem.owner AS remote_owner,"  # 5
							 | 
						||
| 
								 | 
							
								            "\nloc.position as loc_pos,"  # 6
							 | 
						||
| 
								 | 
							
								            "\nrem.position as rem_pos,"  # 7
							 | 
						||
| 
								 | 
							
								            "\nac.search_condition,"  # 8
							 | 
						||
| 
								 | 
							
								            "\nac.delete_rule"  # 9
							 | 
						||
| 
								 | 
							
								            "\nFROM all_constraints%(dblink)s ac,"
							 | 
						||
| 
								 | 
							
								            "\nall_cons_columns%(dblink)s loc,"
							 | 
						||
| 
								 | 
							
								            "\nall_cons_columns%(dblink)s rem"
							 | 
						||
| 
								 | 
							
								            "\nWHERE ac.table_name = CAST(:table_name AS VARCHAR2(128))"
							 | 
						||
| 
								 | 
							
								            "\nAND ac.constraint_type IN ('R','P', 'U', 'C')"
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if schema is not None:
							 | 
						||
| 
								 | 
							
								            params["owner"] = schema
							 | 
						||
| 
								 | 
							
								            text += "\nAND ac.owner = CAST(:owner AS VARCHAR2(128))"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        text += (
							 | 
						||
| 
								 | 
							
								            "\nAND ac.owner = loc.owner"
							 | 
						||
| 
								 | 
							
								            "\nAND ac.constraint_name = loc.constraint_name"
							 | 
						||
| 
								 | 
							
								            "\nAND ac.r_owner = rem.owner(+)"
							 | 
						||
| 
								 | 
							
								            "\nAND ac.r_constraint_name = rem.constraint_name(+)"
							 | 
						||
| 
								 | 
							
								            "\nAND (rem.position IS NULL or loc.position=rem.position)"
							 | 
						||
| 
								 | 
							
								            "\nORDER BY ac.constraint_name, loc.position"
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        text = text % {"dblink": dblink}
							 | 
						||
| 
								 | 
							
								        rp = connection.execute(sql.text(text), params)
							 | 
						||
| 
								 | 
							
								        constraint_data = rp.fetchall()
							 | 
						||
| 
								 | 
							
								        return constraint_data
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @reflection.cache
							 | 
						||
| 
								 | 
							
								    def get_pk_constraint(self, connection, table_name, schema=None, **kw):
							 | 
						||
| 
								 | 
							
								        resolve_synonyms = kw.get("oracle_resolve_synonyms", False)
							 | 
						||
| 
								 | 
							
								        dblink = kw.get("dblink", "")
							 | 
						||
| 
								 | 
							
								        info_cache = kw.get("info_cache")
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        (table_name, schema, dblink, synonym) = self._prepare_reflection_args(
							 | 
						||
| 
								 | 
							
								            connection,
							 | 
						||
| 
								 | 
							
								            table_name,
							 | 
						||
| 
								 | 
							
								            schema,
							 | 
						||
| 
								 | 
							
								            resolve_synonyms,
							 | 
						||
| 
								 | 
							
								            dblink,
							 | 
						||
| 
								 | 
							
								            info_cache=info_cache,
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								        pkeys = []
							 | 
						||
| 
								 | 
							
								        constraint_name = None
							 | 
						||
| 
								 | 
							
								        constraint_data = self._get_constraint_data(
							 | 
						||
| 
								 | 
							
								            connection,
							 | 
						||
| 
								 | 
							
								            table_name,
							 | 
						||
| 
								 | 
							
								            schema,
							 | 
						||
| 
								 | 
							
								            dblink,
							 | 
						||
| 
								 | 
							
								            info_cache=kw.get("info_cache"),
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        for row in constraint_data:
							 | 
						||
| 
								 | 
							
								            (
							 | 
						||
| 
								 | 
							
								                cons_name,
							 | 
						||
| 
								 | 
							
								                cons_type,
							 | 
						||
| 
								 | 
							
								                local_column,
							 | 
						||
| 
								 | 
							
								                remote_table,
							 | 
						||
| 
								 | 
							
								                remote_column,
							 | 
						||
| 
								 | 
							
								                remote_owner,
							 | 
						||
| 
								 | 
							
								            ) = row[0:2] + tuple([self.normalize_name(x) for x in row[2:6]])
							 | 
						||
| 
								 | 
							
								            if cons_type == "P":
							 | 
						||
| 
								 | 
							
								                if constraint_name is None:
							 | 
						||
| 
								 | 
							
								                    constraint_name = self.normalize_name(cons_name)
							 | 
						||
| 
								 | 
							
								                pkeys.append(local_column)
							 | 
						||
| 
								 | 
							
								        return {"constrained_columns": pkeys, "name": constraint_name}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @reflection.cache
							 | 
						||
| 
								 | 
							
								    def get_foreign_keys(self, connection, table_name, schema=None, **kw):
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        kw arguments can be:
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            oracle_resolve_synonyms
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            dblink
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        requested_schema = schema  # to check later on
							 | 
						||
| 
								 | 
							
								        resolve_synonyms = kw.get("oracle_resolve_synonyms", False)
							 | 
						||
| 
								 | 
							
								        dblink = kw.get("dblink", "")
							 | 
						||
| 
								 | 
							
								        info_cache = kw.get("info_cache")
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        (table_name, schema, dblink, synonym) = self._prepare_reflection_args(
							 | 
						||
| 
								 | 
							
								            connection,
							 | 
						||
| 
								 | 
							
								            table_name,
							 | 
						||
| 
								 | 
							
								            schema,
							 | 
						||
| 
								 | 
							
								            resolve_synonyms,
							 | 
						||
| 
								 | 
							
								            dblink,
							 | 
						||
| 
								 | 
							
								            info_cache=info_cache,
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        constraint_data = self._get_constraint_data(
							 | 
						||
| 
								 | 
							
								            connection,
							 | 
						||
| 
								 | 
							
								            table_name,
							 | 
						||
| 
								 | 
							
								            schema,
							 | 
						||
| 
								 | 
							
								            dblink,
							 | 
						||
| 
								 | 
							
								            info_cache=kw.get("info_cache"),
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        def fkey_rec():
							 | 
						||
| 
								 | 
							
								            return {
							 | 
						||
| 
								 | 
							
								                "name": None,
							 | 
						||
| 
								 | 
							
								                "constrained_columns": [],
							 | 
						||
| 
								 | 
							
								                "referred_schema": None,
							 | 
						||
| 
								 | 
							
								                "referred_table": None,
							 | 
						||
| 
								 | 
							
								                "referred_columns": [],
							 | 
						||
| 
								 | 
							
								                "options": {},
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        fkeys = util.defaultdict(fkey_rec)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        for row in constraint_data:
							 | 
						||
| 
								 | 
							
								            (
							 | 
						||
| 
								 | 
							
								                cons_name,
							 | 
						||
| 
								 | 
							
								                cons_type,
							 | 
						||
| 
								 | 
							
								                local_column,
							 | 
						||
| 
								 | 
							
								                remote_table,
							 | 
						||
| 
								 | 
							
								                remote_column,
							 | 
						||
| 
								 | 
							
								                remote_owner,
							 | 
						||
| 
								 | 
							
								            ) = row[0:2] + tuple([self.normalize_name(x) for x in row[2:6]])
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            cons_name = self.normalize_name(cons_name)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            if cons_type == "R":
							 | 
						||
| 
								 | 
							
								                if remote_table is None:
							 | 
						||
| 
								 | 
							
								                    # ticket 363
							 | 
						||
| 
								 | 
							
								                    util.warn(
							 | 
						||
| 
								 | 
							
								                        (
							 | 
						||
| 
								 | 
							
								                            "Got 'None' querying 'table_name' from "
							 | 
						||
| 
								 | 
							
								                            "all_cons_columns%(dblink)s - does the user have "
							 | 
						||
| 
								 | 
							
								                            "proper rights to the table?"
							 | 
						||
| 
								 | 
							
								                        )
							 | 
						||
| 
								 | 
							
								                        % {"dblink": dblink}
							 | 
						||
| 
								 | 
							
								                    )
							 | 
						||
| 
								 | 
							
								                    continue
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                rec = fkeys[cons_name]
							 | 
						||
| 
								 | 
							
								                rec["name"] = cons_name
							 | 
						||
| 
								 | 
							
								                local_cols, remote_cols = (
							 | 
						||
| 
								 | 
							
								                    rec["constrained_columns"],
							 | 
						||
| 
								 | 
							
								                    rec["referred_columns"],
							 | 
						||
| 
								 | 
							
								                )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                if not rec["referred_table"]:
							 | 
						||
| 
								 | 
							
								                    if resolve_synonyms:
							 | 
						||
| 
								 | 
							
								                        (
							 | 
						||
| 
								 | 
							
								                            ref_remote_name,
							 | 
						||
| 
								 | 
							
								                            ref_remote_owner,
							 | 
						||
| 
								 | 
							
								                            ref_dblink,
							 | 
						||
| 
								 | 
							
								                            ref_synonym,
							 | 
						||
| 
								 | 
							
								                        ) = self._resolve_synonym(
							 | 
						||
| 
								 | 
							
								                            connection,
							 | 
						||
| 
								 | 
							
								                            desired_owner=self.denormalize_name(remote_owner),
							 | 
						||
| 
								 | 
							
								                            desired_table=self.denormalize_name(remote_table),
							 | 
						||
| 
								 | 
							
								                        )
							 | 
						||
| 
								 | 
							
								                        if ref_synonym:
							 | 
						||
| 
								 | 
							
								                            remote_table = self.normalize_name(ref_synonym)
							 | 
						||
| 
								 | 
							
								                            remote_owner = self.normalize_name(
							 | 
						||
| 
								 | 
							
								                                ref_remote_owner
							 | 
						||
| 
								 | 
							
								                            )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                    rec["referred_table"] = remote_table
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                    if (
							 | 
						||
| 
								 | 
							
								                        requested_schema is not None
							 | 
						||
| 
								 | 
							
								                        or self.denormalize_name(remote_owner) != schema
							 | 
						||
| 
								 | 
							
								                    ):
							 | 
						||
| 
								 | 
							
								                        rec["referred_schema"] = remote_owner
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                    if row[9] != "NO ACTION":
							 | 
						||
| 
								 | 
							
								                        rec["options"]["ondelete"] = row[9]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                local_cols.append(local_column)
							 | 
						||
| 
								 | 
							
								                remote_cols.append(remote_column)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        return list(fkeys.values())
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @reflection.cache
							 | 
						||
| 
								 | 
							
								    def get_unique_constraints(
							 | 
						||
| 
								 | 
							
								        self, connection, table_name, schema=None, **kw
							 | 
						||
| 
								 | 
							
								    ):
							 | 
						||
| 
								 | 
							
								        resolve_synonyms = kw.get("oracle_resolve_synonyms", False)
							 | 
						||
| 
								 | 
							
								        dblink = kw.get("dblink", "")
							 | 
						||
| 
								 | 
							
								        info_cache = kw.get("info_cache")
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        (table_name, schema, dblink, synonym) = self._prepare_reflection_args(
							 | 
						||
| 
								 | 
							
								            connection,
							 | 
						||
| 
								 | 
							
								            table_name,
							 | 
						||
| 
								 | 
							
								            schema,
							 | 
						||
| 
								 | 
							
								            resolve_synonyms,
							 | 
						||
| 
								 | 
							
								            dblink,
							 | 
						||
| 
								 | 
							
								            info_cache=info_cache,
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        constraint_data = self._get_constraint_data(
							 | 
						||
| 
								 | 
							
								            connection,
							 | 
						||
| 
								 | 
							
								            table_name,
							 | 
						||
| 
								 | 
							
								            schema,
							 | 
						||
| 
								 | 
							
								            dblink,
							 | 
						||
| 
								 | 
							
								            info_cache=kw.get("info_cache"),
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        unique_keys = filter(lambda x: x[1] == "U", constraint_data)
							 | 
						||
| 
								 | 
							
								        uniques_group = groupby(unique_keys, lambda x: x[0])
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        index_names = {
							 | 
						||
| 
								 | 
							
								            ix["name"]
							 | 
						||
| 
								 | 
							
								            for ix in self.get_indexes(connection, table_name, schema=schema)
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        return [
							 | 
						||
| 
								 | 
							
								            {
							 | 
						||
| 
								 | 
							
								                "name": name,
							 | 
						||
| 
								 | 
							
								                "column_names": cols,
							 | 
						||
| 
								 | 
							
								                "duplicates_index": name if name in index_names else None,
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            for name, cols in [
							 | 
						||
| 
								 | 
							
								                [
							 | 
						||
| 
								 | 
							
								                    self.normalize_name(i[0]),
							 | 
						||
| 
								 | 
							
								                    [self.normalize_name(x[2]) for x in i[1]],
							 | 
						||
| 
								 | 
							
								                ]
							 | 
						||
| 
								 | 
							
								                for i in uniques_group
							 | 
						||
| 
								 | 
							
								            ]
							 | 
						||
| 
								 | 
							
								        ]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @reflection.cache
							 | 
						||
| 
								 | 
							
								    def get_view_definition(
							 | 
						||
| 
								 | 
							
								        self,
							 | 
						||
| 
								 | 
							
								        connection,
							 | 
						||
| 
								 | 
							
								        view_name,
							 | 
						||
| 
								 | 
							
								        schema=None,
							 | 
						||
| 
								 | 
							
								        resolve_synonyms=False,
							 | 
						||
| 
								 | 
							
								        dblink="",
							 | 
						||
| 
								 | 
							
								        **kw
							 | 
						||
| 
								 | 
							
								    ):
							 | 
						||
| 
								 | 
							
								        info_cache = kw.get("info_cache")
							 | 
						||
| 
								 | 
							
								        (view_name, schema, dblink, synonym) = self._prepare_reflection_args(
							 | 
						||
| 
								 | 
							
								            connection,
							 | 
						||
| 
								 | 
							
								            view_name,
							 | 
						||
| 
								 | 
							
								            schema,
							 | 
						||
| 
								 | 
							
								            resolve_synonyms,
							 | 
						||
| 
								 | 
							
								            dblink,
							 | 
						||
| 
								 | 
							
								            info_cache=info_cache,
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        params = {"view_name": view_name}
							 | 
						||
| 
								 | 
							
								        text = "SELECT text FROM all_views WHERE view_name=:view_name"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if schema is not None:
							 | 
						||
| 
								 | 
							
								            text += " AND owner = :schema"
							 | 
						||
| 
								 | 
							
								            params["schema"] = schema
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        rp = connection.execute(sql.text(text), params).scalar()
							 | 
						||
| 
								 | 
							
								        if rp:
							 | 
						||
| 
								 | 
							
								            if util.py2k:
							 | 
						||
| 
								 | 
							
								                rp = rp.decode(self.encoding)
							 | 
						||
| 
								 | 
							
								            return rp
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            return None
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @reflection.cache
							 | 
						||
| 
								 | 
							
								    def get_check_constraints(
							 | 
						||
| 
								 | 
							
								        self, connection, table_name, schema=None, include_all=False, **kw
							 | 
						||
| 
								 | 
							
								    ):
							 | 
						||
| 
								 | 
							
								        resolve_synonyms = kw.get("oracle_resolve_synonyms", False)
							 | 
						||
| 
								 | 
							
								        dblink = kw.get("dblink", "")
							 | 
						||
| 
								 | 
							
								        info_cache = kw.get("info_cache")
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        (table_name, schema, dblink, synonym) = self._prepare_reflection_args(
							 | 
						||
| 
								 | 
							
								            connection,
							 | 
						||
| 
								 | 
							
								            table_name,
							 | 
						||
| 
								 | 
							
								            schema,
							 | 
						||
| 
								 | 
							
								            resolve_synonyms,
							 | 
						||
| 
								 | 
							
								            dblink,
							 | 
						||
| 
								 | 
							
								            info_cache=info_cache,
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        constraint_data = self._get_constraint_data(
							 | 
						||
| 
								 | 
							
								            connection,
							 | 
						||
| 
								 | 
							
								            table_name,
							 | 
						||
| 
								 | 
							
								            schema,
							 | 
						||
| 
								 | 
							
								            dblink,
							 | 
						||
| 
								 | 
							
								            info_cache=kw.get("info_cache"),
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        check_constraints = filter(lambda x: x[1] == "C", constraint_data)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        return [
							 | 
						||
| 
								 | 
							
								            {"name": self.normalize_name(cons[0]), "sqltext": cons[8]}
							 | 
						||
| 
								 | 
							
								            for cons in check_constraints
							 | 
						||
| 
								 | 
							
								            if include_all or not re.match(r"..+?. IS NOT NULL$", cons[8])
							 | 
						||
| 
								 | 
							
								        ]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class _OuterJoinColumn(sql.ClauseElement):
							 | 
						||
| 
								 | 
							
								    __visit_name__ = "outer_join_column"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def __init__(self, column):
							 | 
						||
| 
								 | 
							
								        self.column = column
							 |