summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorFederico Caselli <cfederico87@gmail.com>2022-06-03 14:51:04 +0200
committerMike Bayer <mike_mp@zzzcomputing.com>2022-06-07 12:59:57 -0400
commitfcbdae075bb3f3a4ecc9b36e5787bba6b80af9c1 (patch)
tree1df26ffe2f4dece2cc6df4e58cdaee157a2fc010 /lib
parentad86d32f7fbd1c6deda8ff3bebe0595c0f2986cc (diff)
downloadsqlalchemy-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__.py1
-rw-r--r--lib/sqlalchemy/dialects/oracle/cx_oracle.py61
-rw-r--r--lib/sqlalchemy/dialects/oracle/oracledb.py108
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