diff options
| author | Federico Caselli <cfederico87@gmail.com> | 2022-06-03 14:51:04 +0200 |
|---|---|---|
| committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2022-06-07 12:59:57 -0400 |
| commit | fcbdae075bb3f3a4ecc9b36e5787bba6b80af9c1 (patch) | |
| tree | 1df26ffe2f4dece2cc6df4e58cdaee157a2fc010 /lib | |
| parent | ad86d32f7fbd1c6deda8ff3bebe0595c0f2986cc (diff) | |
| download | sqlalchemy-fcbdae075bb3f3a4ecc9b36e5787bba6b80af9c1.tar.gz | |
Add support for the new oracle driver ``oracledb``.
Fixes: #8054
Change-Id: Idd7c1bbb7ca39499f53bdf59a63a6a9d65f144a5
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/sqlalchemy/dialects/oracle/__init__.py | 1 | ||||
| -rw-r--r-- | lib/sqlalchemy/dialects/oracle/cx_oracle.py | 61 | ||||
| -rw-r--r-- | lib/sqlalchemy/dialects/oracle/oracledb.py | 108 |
3 files changed, 140 insertions, 30 deletions
diff --git a/lib/sqlalchemy/dialects/oracle/__init__.py b/lib/sqlalchemy/dialects/oracle/__init__.py index 6b9bbd53d..f1acb4642 100644 --- a/lib/sqlalchemy/dialects/oracle/__init__.py +++ b/lib/sqlalchemy/dialects/oracle/__init__.py @@ -9,6 +9,7 @@ from . import base # noqa from . import cx_oracle # noqa +from . import oracledb # noqa from .base import BFILE from .base import BINARY_DOUBLE from .base import BINARY_FLOAT diff --git a/lib/sqlalchemy/dialects/oracle/cx_oracle.py b/lib/sqlalchemy/dialects/oracle/cx_oracle.py index fbac8b93e..60592253d 100644 --- a/lib/sqlalchemy/dialects/oracle/cx_oracle.py +++ b/lib/sqlalchemy/dialects/oracle/cx_oracle.py @@ -893,7 +893,7 @@ class OracleDialect_cx_oracle(OracleDialect): @util.deprecated_params( threaded=( "1.3", - "The 'threaded' parameter to the cx_oracle dialect " + "The 'threaded' parameter to the cx_oracle/oracledb dialect " "is deprecated as a dialect-level argument, and will be removed " "in a future release. As of version 1.3, it defaults to False " "rather than True. The 'threaded' option can be passed to " @@ -927,40 +927,40 @@ class OracleDialect_cx_oracle(OracleDialect): self.colspecs[sqltypes.Unicode] = _OracleUnicodeStringNCHAR self.colspecs[sqltypes.UnicodeText] = _OracleUnicodeTextNCLOB - cx_Oracle = self.dbapi - - if cx_Oracle is None: - self.cx_oracle_ver = (0, 0, 0) - else: - self.cx_oracle_ver = self._parse_cx_oracle_ver(cx_Oracle.version) - if self.cx_oracle_ver < (7,) and self.cx_oracle_ver > (0, 0, 0): - raise exc.InvalidRequestError( - "cx_Oracle version 7 and above are supported" - ) + dbapi_module = self.dbapi + self._load_version(dbapi_module) + if dbapi_module is not None: self.include_set_input_sizes = { - cx_Oracle.DATETIME, - cx_Oracle.NCLOB, - cx_Oracle.CLOB, - cx_Oracle.LOB, - cx_Oracle.NCHAR, - cx_Oracle.FIXED_NCHAR, - cx_Oracle.BLOB, - cx_Oracle.FIXED_CHAR, - cx_Oracle.TIMESTAMP, + dbapi_module.DATETIME, + dbapi_module.NCLOB, + dbapi_module.CLOB, + dbapi_module.LOB, + dbapi_module.NCHAR, + dbapi_module.FIXED_NCHAR, + dbapi_module.BLOB, + dbapi_module.FIXED_CHAR, + dbapi_module.TIMESTAMP, int, # _OracleInteger, # _OracleBINARY_FLOAT, _OracleBINARY_DOUBLE, - cx_Oracle.NATIVE_FLOAT, + dbapi_module.NATIVE_FLOAT, } self._paramval = lambda value: value.getvalue() - def _parse_cx_oracle_ver(self, version): - m = re.match(r"(\d+)\.(\d+)(?:\.(\d+))?", version) - if m: - return tuple(int(x) for x in m.group(1, 2, 3) if x is not None) - else: - return (0, 0, 0) + def _load_version(self, dbapi_module): + version = (0, 0, 0) + if dbapi_module is not None: + m = re.match(r"(\d+)\.(\d+)(?:\.(\d+))?", dbapi_module.version) + if m: + version = tuple( + int(x) for x in m.group(1, 2, 3) if x is not None + ) + self.cx_oracle_ver = version + if self.cx_oracle_ver < (7,) and self.cx_oracle_ver > (0, 0, 0): + raise exc.InvalidRequestError( + "cx_Oracle version 7 and above are supported" + ) @classmethod def import_dbapi(cls): @@ -969,7 +969,7 @@ class OracleDialect_cx_oracle(OracleDialect): return cx_Oracle def initialize(self, connection): - super(OracleDialect_cx_oracle, self).initialize(connection) + super().initialize(connection) self._detect_decimal_char(connection) def get_isolation_level(self, dbapi_connection): @@ -1163,8 +1163,9 @@ class OracleDialect_cx_oracle(OracleDialect): for opt in ("use_ansi", "auto_convert_lobs"): if opt in opts: util.warn_deprecated( - "cx_oracle dialect option %r should only be passed to " - "create_engine directly, not within the URL string" % opt, + f"{self.driver} dialect option {opt!r} should only be " + "passed to create_engine directly, not within the URL " + "string", version="1.3", ) util.coerce_kw_type(opts, opt, bool) diff --git a/lib/sqlalchemy/dialects/oracle/oracledb.py b/lib/sqlalchemy/dialects/oracle/oracledb.py new file mode 100644 index 000000000..bbe801cd4 --- /dev/null +++ b/lib/sqlalchemy/dialects/oracle/oracledb.py @@ -0,0 +1,108 @@ +# 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 +# mypy: ignore-errors + +r""" +.. dialect:: oracle+oracledb + :name: python-oracledb + :dbapi: oracledb + :connectstring: oracle+oracledb://user:pass@hostname:port[/dbname][?service_name=<service>[&key=value&key=value...]] + :url: https://oracle.github.io/python-oracledb/ + +python-oracledb is released by Oracle to supersede the cx_Oracle driver. +It is fully compatible with cx_Oracle and features both a "thin" client +mode that requires no dependencies, as well as a "thick" mode that uses +the Oracle Client Interface in the same way as cx_Oracle. + +.. seealso:: + + :ref:`cx_oracle` - all of cx_Oracle's notes apply to the oracledb driver + as well. + +Thick mode support +------------------ + +By default the ``python-oracledb`` is started in thin mode, that does not +require oracle client libraries to be installed in the system. The +``python-oracledb`` driver also support a "thick" mode, that behaves +similarly to ``cx_oracle`` and requires that Oracle Client Interface (OCI) +is installed. + +To enable this mode, the user may call ``oracledb.init_oracle_client`` +manually, or by passing the parameter ``thick_mode=True`` to +:func:`_sa.create_engine`. To pass custom arguments to ``init_oracle_client``, +like the ``lib_dir`` path, a dict may be passed to this parameter, as in:: + + engine = sa.create_engine("oracle+oracledb://...", thick_mode={ + "lib_dir": "/path/to/oracle/client/lib", "driver_name": "my-app" + }) + +.. seealso:: + + https://python-oracledb.readthedocs.io/en/latest/api_manual/module.html#oracledb.init_oracle_client + + +.. versionadded:: 2.0.0 added support for oracledb driver. + +""" # noqa +import re + +from .cx_oracle import OracleDialect_cx_oracle as _OracleDialect_cx_oracle +from ... import exc + + +class OracleDialect_oracledb(_OracleDialect_cx_oracle): + supports_statement_cache = True + driver = "oracledb" + + def __init__( + self, + auto_convert_lobs=True, + coerce_to_decimal=True, + arraysize=50, + encoding_errors=None, + thick_mode=None, + **kwargs, + ): + + super().__init__( + auto_convert_lobs, + coerce_to_decimal, + arraysize, + encoding_errors, + **kwargs, + ) + + if thick_mode is not None: + kw = thick_mode if isinstance(thick_mode, dict) else {} + self.dbapi.init_oracle_client(**kw) + + @classmethod + def import_dbapi(cls): + import oracledb + + return oracledb + + @classmethod + def is_thin_mode(cls, connection): + return connection.connection.dbapi_connection.thin + + def _load_version(self, dbapi_module): + version = (0, 0, 0) + if dbapi_module is not None: + m = re.match(r"(\d+)\.(\d+)(?:\.(\d+))?", dbapi_module.version) + if m: + version = tuple( + int(x) for x in m.group(1, 2, 3) if x is not None + ) + self.oracledb_ver = version + if self.oracledb_ver < (1,) and self.oracledb_ver > (0, 0, 0): + raise exc.InvalidRequestError( + "oracledb version 1 and above are supported" + ) + + +dialect = OracleDialect_oracledb |
