/SQLAlchemy-0.7.8/lib/sqlalchemy/dialects/oracle/cx_oracle.py
Python | 757 lines | 663 code | 27 blank | 67 comment | 41 complexity | 9d1fd35ee385210896819d51080e4c38 MD5 | raw file
- # oracle/cx_oracle.py
- # Copyright (C) 2005-2012 the SQLAlchemy authors and contributors <see AUTHORS file>
- #
- # This module is part of SQLAlchemy and is released under
- # the MIT License: http://www.opensource.org/licenses/mit-license.php
- """Support for the Oracle database via the cx_oracle driver.
- Driver
- ------
- The Oracle dialect uses the cx_oracle driver, available at
- http://cx-oracle.sourceforge.net/ . The dialect has several behaviors
- which are specifically tailored towards compatibility with this module.
- Version 5.0 or greater is **strongly** recommended, as SQLAlchemy makes
- extensive use of the cx_oracle output converters for numeric and
- string conversions.
- Connecting
- ----------
- Connecting with create_engine() uses the standard URL approach of
- ``oracle://user:pass@host:port/dbname[?key=value&key=value...]``. If dbname is present, the
- host, port, and dbname tokens are converted to a TNS name using the cx_oracle
- :func:`makedsn()` function. Otherwise, the host token is taken directly as a TNS name.
- Additional arguments which may be specified either as query string arguments on the
- URL, or as keyword arguments to :func:`~sqlalchemy.create_engine()` are:
- * *allow_twophase* - enable two-phase transactions. Defaults to ``True``.
- * *arraysize* - set the cx_oracle.arraysize value on cursors, in SQLAlchemy
- it defaults to 50. See the section on "LOB Objects" below.
- * *auto_convert_lobs* - defaults to True, see the section on LOB objects.
- * *auto_setinputsizes* - the cx_oracle.setinputsizes() call is issued for all bind parameters.
- This is required for LOB datatypes but can be disabled to reduce overhead. Defaults
- to ``True``.
- * *mode* - This is given the string value of SYSDBA or SYSOPER, or alternatively an
- integer value. This value is only available as a URL query string argument.
- * *threaded* - enable multithreaded access to cx_oracle connections. Defaults
- to ``True``. Note that this is the opposite default of cx_oracle itself.
- Unicode
- -------
- cx_oracle 5 fully supports Python unicode objects. SQLAlchemy will pass
- all unicode strings directly to cx_oracle, and additionally uses an output
- handler so that all string based result values are returned as unicode as well.
- Generally, the ``NLS_LANG`` environment variable determines the nature
- of the encoding to be used.
- Note that this behavior is disabled when Oracle 8 is detected, as it has been
- observed that issues remain when passing Python unicodes to cx_oracle with Oracle 8.
- LOB Objects
- -----------
- cx_oracle returns oracle LOBs using the cx_oracle.LOB object. SQLAlchemy converts
- these to strings so that the interface of the Binary type is consistent with that of
- other backends, and so that the linkage to a live cursor is not needed in scenarios
- like result.fetchmany() and result.fetchall(). This means that by default, LOB
- objects are fully fetched unconditionally by SQLAlchemy, and the linkage to a live
- cursor is broken.
- To disable this processing, pass ``auto_convert_lobs=False`` to :func:`create_engine()`.
- Two Phase Transaction Support
- -----------------------------
- Two Phase transactions are implemented using XA transactions. Success has been reported
- with this feature but it should be regarded as experimental.
- Precision Numerics
- ------------------
- The SQLAlchemy dialect goes through a lot of steps to ensure
- that decimal numbers are sent and received with full accuracy.
- An "outputtypehandler" callable is associated with each
- cx_oracle connection object which detects numeric types and
- receives them as string values, instead of receiving a Python
- ``float`` directly, which is then passed to the Python
- ``Decimal`` constructor. The :class:`.Numeric` and
- :class:`.Float` types under the cx_oracle dialect are aware of
- this behavior, and will coerce the ``Decimal`` to ``float`` if
- the ``asdecimal`` flag is ``False`` (default on :class:`.Float`,
- optional on :class:`.Numeric`).
- Because the handler coerces to ``Decimal`` in all cases first,
- the feature can detract significantly from performance.
- If precision numerics aren't required, the decimal handling
- can be disabled by passing the flag ``coerce_to_decimal=False``
- to :func:`.create_engine`::
- engine = create_engine("oracle+cx_oracle://dsn",
- coerce_to_decimal=False)
- .. versionadded:: 0.7.6
- Add the ``coerce_to_decimal`` flag.
- Another alternative to performance is to use the
- `cdecimal <http://pypi.python.org/pypi/cdecimal/>`_ library;
- see :class:`.Numeric` for additional notes.
- The handler attempts to use the "precision" and "scale"
- attributes of the result set column to best determine if
- subsequent incoming values should be received as ``Decimal`` as
- opposed to int (in which case no processing is added). There are
- several scenarios where OCI_ does not provide unambiguous data
- as to the numeric type, including some situations where
- individual rows may return a combination of floating point and
- integer values. Certain values for "precision" and "scale" have
- been observed to determine this scenario. When it occurs, the
- outputtypehandler receives as string and then passes off to a
- processing function which detects, for each returned value, if a
- decimal point is present, and if so converts to ``Decimal``,
- otherwise to int. The intention is that simple int-based
- statements like "SELECT my_seq.nextval() FROM DUAL" continue to
- return ints and not ``Decimal`` objects, and that any kind of
- floating point value is received as a string so that there is no
- floating point loss of precision.
- The "decimal point is present" logic itself is also sensitive to
- locale. Under OCI_, this is controlled by the NLS_LANG
- environment variable. Upon first connection, the dialect runs a
- test to determine the current "decimal" character, which can be
- a comma "," for european locales. From that point forward the
- outputtypehandler uses that character to represent a decimal
- point. Note that cx_oracle 5.0.3 or greater is required
- when dealing with numerics with locale settings that don't use
- a period "." as the decimal character.
- .. versionchanged:: 0.6.6
- The outputtypehandler uses a comma "," character to represent
- a decimal point.
- .. _OCI: http://www.oracle.com/technetwork/database/features/oci/index.html
- """
- from sqlalchemy.dialects.oracle.base import OracleCompiler, OracleDialect, \
- RESERVED_WORDS, OracleExecutionContext
- from sqlalchemy.dialects.oracle import base as oracle
- from sqlalchemy.engine import base
- from sqlalchemy import types as sqltypes, util, exc, processors
- from datetime import datetime
- import random
- import collections
- from sqlalchemy.util.compat import decimal
- import re
- class _OracleNumeric(sqltypes.Numeric):
- def bind_processor(self, dialect):
- # cx_oracle accepts Decimal objects and floats
- return None
- def result_processor(self, dialect, coltype):
- # we apply a cx_oracle type handler to all connections
- # that converts floating point strings to Decimal().
- # However, in some subquery situations, Oracle doesn't
- # give us enough information to determine int or Decimal.
- # It could even be int/Decimal differently on each row,
- # regardless of the scale given for the originating type.
- # So we still need an old school isinstance() handler
- # here for decimals.
- if dialect.supports_native_decimal:
- if self.asdecimal:
- if self.scale is None:
- fstring = "%.10f"
- else:
- fstring = "%%.%df" % self.scale
- def to_decimal(value):
- if value is None:
- return None
- elif isinstance(value, decimal.Decimal):
- return value
- else:
- return decimal.Decimal(fstring % value)
- return to_decimal
- else:
- if self.precision is None and self.scale is None:
- return processors.to_float
- elif not getattr(self, '_is_oracle_number', False) \
- and self.scale is not None:
- return processors.to_float
- else:
- return None
- else:
- # cx_oracle 4 behavior, will assume
- # floats
- return super(_OracleNumeric, self).\
- result_processor(dialect, coltype)
- class _OracleDate(sqltypes.Date):
- def bind_processor(self, dialect):
- return None
- def result_processor(self, dialect, coltype):
- def process(value):
- if value is not None:
- return value.date()
- else:
- return value
- return process
- class _LOBMixin(object):
- def result_processor(self, dialect, coltype):
- if not dialect.auto_convert_lobs:
- # return the cx_oracle.LOB directly.
- return None
- def process(value):
- if value is not None:
- return value.read()
- else:
- return value
- return process
- class _NativeUnicodeMixin(object):
- # Py3K
- #pass
- # Py2K
- def bind_processor(self, dialect):
- if dialect._cx_oracle_with_unicode:
- def process(value):
- if value is None:
- return value
- else:
- return unicode(value)
- return process
- else:
- return super(_NativeUnicodeMixin, self).bind_processor(dialect)
- # end Py2K
- # we apply a connection output handler that returns
- # unicode in all cases, so the "native_unicode" flag
- # will be set for the default String.result_processor.
- class _OracleChar(_NativeUnicodeMixin, sqltypes.CHAR):
- def get_dbapi_type(self, dbapi):
- return dbapi.FIXED_CHAR
- class _OracleNVarChar(_NativeUnicodeMixin, sqltypes.NVARCHAR):
-