Compare commits

..

13 Commits
main ... v1.2.x

Author SHA1 Message Date
Anthony Tuininga ec62806c18 Bump copyright. 2023-01-19 09:58:59 -07:00
Anthony Tuininga 33f4093ad1 Fixed bug when attempting to populate an array variable with too many
elements.
2023-01-18 16:12:13 -07:00
Anthony Tuininga cd70b4474b Some databases return a negative length instead of a 0 length when
returning null boolean values (#119).
2023-01-18 13:55:35 -07:00
Anthony Tuininga 104ec0331b Preparing to release version 1.2.2. 2023-01-18 10:06:04 -07:00
Anthony Tuininga 23af6a748e Fixed bug when using a "select * from table" query and columns are added
to the table (#125).
2023-01-18 10:05:23 -07:00
Anthony Tuininga 7076712234 Update documentation. 2023-01-18 10:05:00 -07:00
Anthony Tuininga ecd8956d42 Fixed bug when getting a record type based on a table (%ROWTYPE) (#123). 2023-01-18 10:04:36 -07:00
Anthony Tuininga d73e43acad Fixed bug when attempting to create bequeath connections to a local
database (#114).
2023-01-18 10:03:45 -07:00
Anthony Tuininga cc52daf97b Any exception raised attempting to find the logged on user for logging
purposes is now ignored (#112).
2023-01-18 10:03:20 -07:00
Anthony Tuininga cebde524d0 Fixed bug when binding OUT a NULL boolean value (#119). 2023-01-18 10:02:55 -07:00
Anthony Tuininga b07a524373 Show how to correctly bind data to insert into NVARCHAR2 columns. 2023-01-18 10:02:37 -07:00
Anthony Tuininga 1d1e17144d Mention DPI-1047 for SEO. Mention unsafe paths. 2023-01-18 10:02:05 -07:00
Anthony Tuininga ace0d52ce7 Bump version. 2023-01-18 10:01:51 -07:00
364 changed files with 15476 additions and 43206 deletions

View File

@ -7,9 +7,9 @@ and privacy of all our users.
Please do NOT raise a GitHub Issue to report a security vulnerability. If you
believe you have found a security vulnerability, please submit a report to
[secalert_us@oracle.com][1] preferably with a proof of concept. Please review
some additional information on [how to report security vulnerabilities to
Oracle][2]. We encourage people who contact Oracle Security to use email
encryption using [our encryption key][3].
some additional information on [how to report security vulnerabilities to Oracle][2].
We encourage people who contact Oracle Security to use email encryption using
[our encryption key][3].
We ask that you do not use other channels or contact the project maintainers
directly.
@ -20,16 +20,17 @@ security features are welcome on GitHub Issues.
## Security updates, alerts and bulletins
Security updates will be released on a regular cadence. Many of our projects
will typically release security fixes in conjunction with the Oracle Critical
Patch Update program. Additional information, including past advisories, is
available on our [security alerts][4] page.
will typically release security fixes in conjunction with the
[Oracle Critical Patch Update][3] program. Additional
information, including past advisories, is available on our [security alerts][4]
page.
## Security-related information
We will provide security related information such as a threat model,
considerations for secure use, or any known security issues in our
documentation. Please note that labs and sample code are intended to
demonstrate a concept and may not be sufficiently hardened for production use.
We will provide security related information such as a threat model, considerations
for secure use, or any known security issues in our documentation. Please note
that labs and sample code are intended to demonstrate a concept and may not be
sufficiently hardened for production use.
[1]: mailto:secalert_us@oracle.com
[2]: https://www.oracle.com/corporate/security-practices/assurance/vulnerability/reporting.html

1
.gitignore vendored
View File

@ -6,4 +6,3 @@ build/
dist/
doc/build
src/oracledb/*.c
.ipynb_checkpoints/

View File

@ -1,16 +0,0 @@
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.4.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-yaml
- id: check-added-large-files
- repo: https://github.com/psf/black-pre-commit-mirror
rev: 23.9.1
hooks:
- id: black
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.0.291
hooks:
- id: ruff

View File

@ -1,58 +1,51 @@
# Contributing to this repository
# Contributing
We welcome your contributions! There are multiple ways to contribute.
## Opening issues
## Issues
For bugs or enhancement requests, please file a GitHub issue unless it's
security related. When filing a bug remember that the better written the bug is,
the more likely it is to be fixed. If you think you've found a security
vulnerability, do not raise a GitHub issue and follow the instructions in our
[security policy](./SECURITY.md).
security related. When filing a bug remember that the better written the bug
is, the more likely it is to be fixed. If you think you've found a security
vulnerability, do not raise a GitHub issue and follow the instructions on our
[Security Policy](./.github/SECURITY.md).
## Contributing code
## Contributing Code
We welcome your code contributions. Before submitting code via a pull request,
you will need to have signed the [Oracle Contributor Agreement][OCA] (OCA) and
your commits need to include the following line using the name and e-mail
address you used to sign the OCA:
To get started, you will need to sign the [Oracle Contributor
Agreement](https://oca.opensource.oracle.com) (OCA).
For pull requests to be accepted, the bottom of your commit message must have
the following line using the name and e-mail address you used for the OCA.
```text
Signed-off-by: Your Name <you@example.org>
```
This can be automatically added to pull requests by committing with
`--sign-off` or `-s`, e.g.
This can be automatically added to pull requests by committing with:
```text
git commit --signoff
```
Only pull requests from committers that can be verified as having signed the
OCA can be accepted.
Only pull requests from committers that can be verified as having
signed the OCA can be accepted.
## Pull request process
### Pull request process
1. Ensure there is an issue created to track and discuss the fix or enhancement
you intend to submit.
1. Fork this repository.
1. Fork this repository
1. Create a branch in your fork to implement the changes. We recommend using
the issue number as part of your branch name, e.g. `1234-fixes`.
the issue number as part of your branch name, e.g. `1234-fixes`
1. Ensure that any documentation is updated with the changes that are required
by your change.
by your fix.
1. Ensure that any samples are updated if the base image has been changed.
1. Submit the pull request. *Do not leave the pull request blank*. Explain exactly
what your changes are meant to do and provide simple steps on how to validate.
your changes. Ensure that you reference the issue you created as well.
1. We will assign the pull request to 2-3 people for review before it is merged.
what your changes are meant to do and provide simple steps on how to validate
your changes. Ensure that you reference the issue you created as well.
1. We will review your PR before it is merged.
Note we merge to an internal repo first before pushing back to GitHub.
## Code of conduct
## Code of Conduct
Follow the [Golden Rule](https://en.wikipedia.org/wiki/Golden_Rule). If you'd
like more specific guidelines, see the [Contributor Covenant Code of
Conduct][COC].
[OCA]: https://oca.opensource.oracle.com
[COC]: https://www.contributor-covenant.org/version/1/4/code-of-conduct/
like more specific guidelines see the [Contributor Covenant Code of
Conduct](https://www.contributor-covenant.org/version/1/4/code-of-conduct/)

View File

@ -1,4 +1,4 @@
Copyright (c) 2016, 2023 Oracle and/or its affiliates.
Copyright (c) 2016, 2022 Oracle and/or its affiliates.
This software is dual-licensed to you under the Universal Permissive License
(UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl and Apache License

View File

@ -1 +1 @@
Copyright (c) 2016, 2023, Oracle and/or its affiliates.
Copyright (c) 2016, 2022, Oracle and/or its affiliates.

View File

@ -8,8 +8,6 @@ The module conforms to the [Python Database API 2.0 specification][pep249] with
a considerable number of additions and a couple of minor exclusions, see the
[feature list][features].
Synchronous and [concurrent][concurrent] coding styles are supported.
## Installation
Run `python -m pip install oracledb`
@ -18,10 +16,10 @@ See [python-oracledb Installation][installation].
## Dependencies and Interoperability
- Python versions 3.7 through 3.12.
- Python versions 3.6 through 3.11.
Prebuilt packages are available for these Python versions on Windows, on
macOS and on Linux.
Prebuilt packages are available on Windows for Python 3.7 or later, on macOS
for Python 3.7 or later, and on Linux for Python 3.6 or later.
Source code is also available.
@ -68,18 +66,11 @@ See [/tests][tests]
## Contributing
This project welcomes contributions from the community. Before submitting a
pull request, please [review our contribution guide](./CONTRIBUTING.md).
## Security
Please consult the [security guide](./SECURITY.md) for our responsible security
vulnerability disclosure process.
See [CONTRIBUTING](https://github.com/oracle/python-oracledb/blob/main/CONTRIBUTING.md)
## License
See [LICENSE][license], [THIRD_PARTY_LICENSES][tplicense], and
[NOTICE][notice].
See [LICENSE][license], [THIRD_PARTY_LICENSES][tplicense], and [NOTICE][notice].
[python]: https://www.python.org/
[oracledb]: https://www.oracle.com/database/
@ -97,4 +88,3 @@ See [LICENSE][license], [THIRD_PARTY_LICENSES][tplicense], and
[samples]: https://github.com/oracle/python-oracledb/tree/main/samples
[installation]: https://python-oracledb.readthedocs.io/en/latest/user_guide/installation.html
[features]: https://oracle.github.io/python-oracledb/#features
[concurrent]: https://python-oracledb.readthedocs.io/en/latest/user_guide/asyncio.html

View File

@ -2,3 +2,4 @@ Please see the python-oracledb home page for links to documentation,
installation instructions, and source code:
https://oracle.github.io/python-oracledb/index.html

View File

@ -453,3 +453,4 @@ GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@ -1,2 +1,2 @@
sphinx>=4.2.0
sphinx-rtd-theme>=0.5.2
sphinx-rtd-theme

View File

@ -1,5 +1,5 @@
# -----------------------------------------------------------------------------
# Copyright (c) 2022, 2023, Oracle and/or its affiliates.
#------------------------------------------------------------------------------
# Copyright (c) 2022, Oracle and/or its affiliates.
#
# This software is dual-licensed to you under the Universal Permissive License
# (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl and Apache License
@ -20,21 +20,20 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# -----------------------------------------------------------------------------
#------------------------------------------------------------------------------
# -----------------------------------------------------------------------------
#------------------------------------------------------------------------------
# oracle_deprecated.py
#
# Overrides the 'deprecated' directive so that a componment name can be used in
# conjunction with the version.
# -----------------------------------------------------------------------------
# Overrides the 'deprecated' directive so that it can a componment name can be
# used in conjunction with the version.
#------------------------------------------------------------------------------
from docutils import nodes
from docutils.parsers.rst import Directive
from sphinx import addnodes
class DriverDeprecated(Directive):
has_content = True
required_arguments = 1
@ -44,22 +43,22 @@ class DriverDeprecated(Directive):
def run(self):
node = addnodes.versionmodified()
node.document = self.state.document
node["type"] = self.name
node["version"] = self.arguments[0]
text = "Deprecated since {}.".format(self.arguments[0])
classes = ["versionmodified", "deprecated"]
para = nodes.paragraph(
"", "", nodes.inline("", text, classes=classes), translatable=False
)
node['type'] = self.name
node['version'] = self.arguments[0]
text = 'Deprecated since {}.'.format(self.arguments[0])
classes = ['versionmodified', 'deprecated']
para = nodes.paragraph('', '',
nodes.inline('', text, classes=classes),
translatable=False)
node.append(para)
return [node]
ret: List[Node] = [node]
return ret
def setup(app):
app.add_directive("deprecated", DriverDeprecated, override=True)
return {
"version": "0.1",
"parallel_read_safe": True,
"parallel_write_safe": True,
'version': '0.1',
'parallel_read_safe': True,
'parallel_write_safe': True,
}

View File

@ -1,4 +1,4 @@
# -----------------------------------------------------------------------------
#------------------------------------------------------------------------------
# Copyright (c) 2022, Oracle and/or its affiliates.
#
# This software is dual-licensed to you under the Universal Permissive License
@ -20,14 +20,14 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# -----------------------------------------------------------------------------
#------------------------------------------------------------------------------
# -----------------------------------------------------------------------------
#------------------------------------------------------------------------------
# table_with_summary.py
#
# Defines a directive (list-table-with-summary) that adds support for a summary
# option on top of the regular "list-table" directive.
# -----------------------------------------------------------------------------
#------------------------------------------------------------------------------
import sphinx
from docutils.parsers.rst import directives
@ -35,14 +35,14 @@ from docutils.parsers.rst.directives import tables
from packaging import version
class ListTableWithSummary(tables.ListTable):
option_spec = {"summary": directives.unchanged}
option_spec = {'summary': directives.unchanged}
option_spec.update(tables.ListTable.option_spec)
def run(self):
result = super().run()
summary = self.options.get("summary")
summary = self.options.get('summary')
if summary:
table_node = result[0]
table_node["summary"] = summary
@ -50,29 +50,29 @@ class ListTableWithSummary(tables.ListTable):
class HTMLTranslator(sphinx.writers.html5.HTML5Translator):
def visit_table(self, node):
if version.parse(sphinx.__version__) > version.parse("4.2.0"):
if version.parse(sphinx.__version__) > version.parse('4.2.0'):
self._table_row_indices = [0]
else:
self._table_row_index = 0
atts = {}
classes = [
cls.strip(" \t\n") for cls in self.settings.table_style.split(",")
]
classes = [cls.strip(' \t\n') \
for cls in self.settings.table_style.split(',')]
classes.insert(0, "docutils") # compat
# set align-default if align not specified to give a default style
classes.append("align-%s" % node.get("align", "default"))
classes.append('align-%s' % node.get('align', 'default'))
if "width" in node:
atts["style"] = "width: %s" % node["width"]
if "summary" in node:
atts["summary"] = node["summary"]
tag = self.starttag(node, "table", CLASS=" ".join(classes), **atts)
if 'width' in node:
atts['style'] = 'width: %s' % node['width']
if 'summary' in node:
atts['summary'] = node['summary']
tag = self.starttag(node, 'table', CLASS=' '.join(classes), **atts)
self.body.append(tag)
def setup(app):
app.add_directive("list-table-with-summary", ListTableWithSummary)
app.add_directive('list-table-with-summary', ListTableWithSummary)
app.set_translator("html", HTMLTranslator, override=True)

View File

@ -340,7 +340,7 @@ Message Properties
:data:`~oracledb.MSG_READY`, :data:`~oracledb.MSG_PROCESSED` or
:data:`~oracledb.MSG_EXPIRED`.
.. attribute:: MessageProperties.recipients
.. attribute:: Messageproperties.recipient
This read-write attribute specifies a list of recipient names that can be
associated with a message at the time of enqueuing the message. This allows a

View File

@ -1,334 +0,0 @@
.. _asyncconnobj:
****************************
API: AsyncConnection Objects
****************************
An AsyncConnection object can be created with :meth:`oracledb.connect_async()`
or with :meth:`AsyncConnectionPool.acquire()`. AsyncConnections support use of
concurrent programming with `asyncio <https://docs.python.org/3/library/
asyncio.html>`__. Unless explicitly noted as synchronous, the AsyncConnection
methods should be used with ``await``. This object is an extension to the DB
API.
.. versionadded:: 2.0.0
.. note::
The Asynchronous I/O (asyncio) support in python-oracledb 2.0.0 is a
pre-release and may change in the next version.
.. note::
AsyncConnection objects are only supported in the python-oracledb Thin
mode.
.. note::
Any outstanding database transaction will be rolled back when the
connection object is destroyed or closed. You must perform a
:meth:`commit <AsyncConnection.commit>` first if you want data to
persist in the database, see :ref:`txnasync`.
.. _asyncconnmeth:
AsyncConnection Methods
=======================
.. method:: AsyncConnection.__aenter__()
The entry point for the asynchronous connection as a context manager. It
returns itself.
.. method:: AsyncConnection.__aexit__()
The exit point for the asynchronous connection as a context manager. This
will close the connection and roll back any uncommitted transaction.
.. method:: AsyncConnection.callfunc(name, return_type, parameters=[], \
keyword_parameters={})
Calls a PL/SQL function with the given name.
This is a shortcut for creating a cursor, calling the stored function with
the cursor, and then closing the cursor.
.. method:: AsyncConnection.callproc(name, parameters=[], \
keyword_parameters={})
Calls a PL/SQL procedure with the given name.
This is a shortcut for creating a cursor, calling the stored procedure
with the cursor, and then closing the cursor.
.. method:: AsyncConnection.cancel()
A synchronous method that breaks a long-running statement.
.. method:: AsyncConnection.changepassword(old_password, new_password)
Changes the password for the user to which the connection is connected.
.. method:: AsyncConnection.close()
Closes the connection.
.. method:: AsyncConnection.commit()
Commits any pending transaction to the database.
.. method:: AsyncConnection.createlob(lob_type)
Creates and returns a new temporary LOB of the specified type.
.. method:: AsyncConnection.cursor(scrollable=False)
A synchronous method that returns a cursor associated with the connection.
.. method:: AsyncConnection.execute(statement, parameters=[])
Executes a statement against the database.
This is a shortcut for creating a cursor, executing a statement with the
cursor, and then closing the cursor.
.. method:: AsyncConnection.executemany(statement, parameters=[])
Prepares a statement for execution against a database and then executes it
against all parameter mappings or sequences found in the sequence
parameters.
This is a shortcut for creating a cursor, calling
:meth:`AsyncCursor.executemany()` on the cursor, and then closing the
cursor.
.. method:: AsyncConnection.fetchall(statement, parameters=None, \
arraysize=None, rowfactory=None)
Executes a query and returns all of the rows. After the rows are
fetched, the cursor is closed.
.. method:: AsyncConnection.fetchmany(statement, parameters=None, \
num_rows=None, rowfactory=None)
Executes a query and returns up to the specified number of rows. After the
rows are fetched, the cursor is closed.
.. method:: AsyncConnection.fetchone(statement, parameters=None, \
rowfactory=None)
Executes a query and returns the first row of the result set if one exists
(or None if no rows exist). After the row is fetched, the cursor is
closed.
.. method:: AsyncConnection.gettype(name)
Returns a :ref:`type object <dbobjecttype>` given its name. This can then
be used to create objects which can be bound to cursors created by this
connection.
.. method:: AsyncConnection.is_healthy()
A synchronous method that returns a boolean indicating the health status
of a connection.
Connections may become unusable in several cases, such as, if the network
socket is broken, if an Oracle error indicates the connection is unusable,
or, after receiving a planned down notification from the database.
This function is best used before starting a new database request on an
existing standalone connection. Pooled connections internally perform this
check before returning a connection to the application.
If this function returns False, the connection should be not be used by the
application and a new connection should be established instead.
This function performs a local check. To fully check a connection's health,
use :meth:`AsyncConnection.ping()` which performs a round-trip to the
database.
.. method:: AsyncConnection.ping()
Pings the database to verify if the connection is valid.
.. method:: AsyncConnection.rollback()
Rolls back any pending transaction.
.. _asynconnattr:
AsyncConnection Attributes
==========================
.. attribute:: AsyncConnection.action
This write-only attribute sets the action column in the v$session table. It
is a string attribute but the value None is accepted and treated as an
empty string.
.. attribute:: AsyncConnection.autocommit
This read-write attribute determines whether autocommit mode is on or off.
When autocommit mode is on, all statements are committed as soon as they
have completed executing.
.. attribute:: AsyncConnection.call_timeout
This read-write attribute specifies the amount of time (in milliseconds)
that a single round-trip to the database may take before a timeout will
occur. A value of 0 means that no timeout will take place.
If a timeout occurs, the error *DPI-1067* will be returned if the
connection is still usable. Alternatively the error *DPI-1080* will be
returned if the connection has become invalid and can no longer be used.
.. attribute:: AsyncConnection.client_identifier
This write-only attribute sets the client_identifier column in the
v$session table.
.. attribute:: AsyncConnection.clientinfo
This write-only attribute sets the client_info column in the v$session
table.
.. attribute:: AsyncConnection.current_schema
This read-write attribute sets the current schema attribute for the
session. Setting this value is the same as executing the SQL statement
``ALTER SESSION SET CURRENT_SCHEMA``. The attribute is set (and verified) on
the next call that does a round trip to the server. The value is placed
before unqualified database objects in SQL statements you then execute.
.. attribute:: AsyncConnection.db_domain
This read-only attribute specifies the Oracle Database domain name
associated with the connection. It is the same value returned by the SQL
``SELECT value FROM V$PARAMETER WHERE NAME = 'db_domain'``.
.. attribute:: AsyncConnection.db_name
This read-only attribute specifies the Oracle Database name associated with
the connection. It is the same value returned by the SQL
``SELECT NAME FROM V$DATABASE``.
.. attribute:: AsyncConnection.dbop
This write-only attribute sets the database operation that is to be
monitored. This can be viewed in the ``DBOP_NAME`` column of the
``v$sql_monitor`` table.
.. attribute:: AsyncConnection.dsn
This read-only attribute returns the TNS entry of the database to which a
connection has been established.
.. attribute:: AsyncConnection.econtext_id
This write-only attribute specifies the execution context id. This
value can be found as ecid in the v$session table and econtext_id in the
auditing tables. The maximum length is 64 bytes.
.. attribute:: AsyncConnection.edition
This read-only attribute gets the session edition and is only available in
Oracle Database 11.2 (the server must be at this level or higher for this
to work). This attribute is ignored in python-oracledb Thin mode.
.. attribute:: AsyncConnection.external_name
This read-write attribute specifies the external name that is used by the
connection when logging distributed transactions.
.. attribute:: AsyncConnection.inputtypehandler
This read-write attribute specifies a method called for each value that is
bound to a statement executed on any cursor associated with this
connection. The method signature is handler(cursor, value, arraysize) and
the return value is expected to be a variable object or None in which case
a default variable object will be created. If this attribute is None, the
default behavior will take place for all values bound to statements.
.. attribute:: AsyncConnection.instance_name
This read-only attribute specifies the Oracle Database instance name
associated with the connection. It is the same value as the SQL expression
``sys_context('userenv', 'instance_name')``.
.. attribute:: AsyncConnection.internal_name
This read-write attribute specifies the internal name that is used by the
connection when logging distributed transactions.
.. attribute:: AsyncConnection.ltxid
This read-only attribute returns the logical transaction id for the
connection. It is used within Oracle Transaction Guard as a means of
ensuring that transactions are not duplicated. See the Oracle documentation
and the provided sample for more information.
.. note:
This attribute is only available when Oracle Database 12.1 or later is
in use
.. attribute:: AsyncConnection.max_open_cursors
This read-only attribute specifies the maximum number of cursors that the
database can have open concurrently. It is the same value returned by the
SQL ``SELECT VALUE FROM V$PARAMETER WHERE NAME = 'open_cursors'``.
.. attribute:: AsyncConnection.module
This write-only attribute sets the module column in the v$session table.
The maximum length for this string is 48 and if you exceed this length you
will get ORA-24960.
.. attribute:: AsyncConnection.outputtypehandler
This read-write attribute specifies a method called for each column that is
going to be fetched from any cursor associated with this connection. The
method signature is ``handler(cursor, metadata)`` and the return value is
expected to be a :ref:`variable object<varobj>` or None in which case a
default variable object will be created. If this attribute is None, the
default behavior will take place for all columns fetched from cursors.
See :ref:`outputtypehandlers`.
.. attribute:: AsyncConnection.sdu
This read-only attribute specifies the size of the Session Data Unit (SDU)
that is being used by the connection. The value will be the lesser of the
requested python-oracledb size and the maximum size allowed by the database
network configuration.
.. attribute:: AsyncConnection.service_name
This read-only attribute specifies the Oracle Database service name
associated with the connection. This is the same value returned by the SQL
``SELECT SYS_CONTEXT('USERENV', 'SERVICE_NAME') FROM DUAL``.
.. attribute:: AsyncConnection.stmtcachesize
This read-write attribute specifies the size of the statement cache. This
value can make a significant difference in performance if you have a small
number of statements that you execute repeatedly.
The default value is 20.
See :ref:`Statement Caching <stmtcache>` for more information.
.. attribute:: AsyncConnection.transaction_in_progress
This read-only attribute specifies whether a transaction is currently in
progress on the database associated with the connection.
.. attribute:: AsyncConnection.username
This read-only attribute returns the name of the user which established the
connection to the database.
.. attribute:: AsyncConnection.version
This read-only attribute returns the version of the database to which a
connection has been established.

View File

@ -1,213 +0,0 @@
.. _asyncconnpoolobj:
********************************
API: AsyncConnectionPool Objects
********************************
An AsyncConnectionPool object can be created with
:meth:`oracledb.create_pool_async()`. This object is an extension to the DB
API.
.. versionadded:: 2.0.0
.. note::
The Asynchronous I/O (asyncio) support in python-oracledb 2.0.0 is a
pre-release and may change in the next version.
.. note::
AsyncConnectionPool objects are only supported in the python-oracledb Thin
mode.
.. _asynconnpoolmeth:
AsyncConnectionPool Methods
===========================
.. method:: AsyncConnectionPool.acquire(user=None, password=None, cclass=None, \
purity=oracledb.PURITY_DEFAULT, tag=None, matchanytag=False, \
shardingkey=[], supershardingkey=[])
Acquires a connection from the pool and returns an
:ref:`asynchronous connection object <asyncconnobj>`.
If the pool is :ref:`homogeneous <connpooltypes>`, the ``user`` and
``password`` parameters cannot be specified. If they are, an exception will
be raised.
The ``cclass`` parameter, if specified, should be a string corresponding to
the connection class for Database Resident Connection Pooling (DRCP).
The ``purity`` parameter is expected to be one of
:data:`~oracledb.PURITY_NEW`, :data:`~oracledb.PURITY_ANY`, or
:data:`~oracledb.PURITY_DEFAULT`.
The ``tag``, ``matchanytag``, ``shardingkey``, and ``supershardingkey``
parameters are ignored in python-oracledb Thin mode.
.. method:: AsyncConnectionPool.close(force=False)
Closes the pool now, rather than when the last reference to it is
released, which makes it unusable for further work.
If any connections have been acquired and not released back to the pool,
this method will fail unless the ``force`` parameter is set to *True*.
.. method:: AsyncConnectionPool.drop(connection)
Drops the connection from the pool which is useful if the connection is no
longer usable (such as when the session is killed).
.. method:: AsyncConnectionPool.release(connection, tag=None)
Releases the connection back to the pool now, rather than whenever
``__del__`` is called. The connection will be unusable from this point
forward; an Error exception will be raised if any operation is attempted
with the connection. Any cursors or LOBs created by the connection will
also be marked unusable and an Error exception will be raised if any
operation is attempted with them.
Internally, references to the connection are held by cursor objects,
LOB objects, and so on. Once all of these references are released, the
connection itself will be released back to the pool automatically. Either
control references to these related objects carefully or explicitly
release connections back to the pool in order to ensure sufficient
resources are available.
The ``tag`` parameter is ignored in python-oracledb Thin mode.
.. _asyncconnpoolattr:
AsyncConnectionPool Attributes
==============================
.. attribute:: AsyncConnectionPool.busy
This read-only attribute returns the number of connections currently
acquired.
.. attribute:: AsyncConnectionPool.dsn
This read-only attribute returns the TNS entry of the database to which a
connection has been established.
.. attribute:: AsyncConnectionPool.getmode
This read-write attribute determines how connections are returned from the
pool. If :data:`~oracledb.POOL_GETMODE_FORCEGET` is specified, a new
connection will be returned even if there are no free connections in the
pool. :data:`~oracledb.POOL_GETMODE_NOWAIT` will raise an exception if
there are no free connections are available in the pool. If
:data:`~oracledb.POOL_GETMODE_WAIT` is specified and there are no free
connections in the pool, the caller will wait until a free connection is
available. :data:`~oracledb.POOL_GETMODE_TIMEDWAIT` uses the value of
:data:`~ConnectionPool.wait_timeout` to determine how long the caller
should wait for a connection to become available before returning an error.
.. attribute:: AsyncConnectionPool.homogeneous
This read-only boolean attribute indicates whether the pool is considered
:ref:`homogeneous <connpooltypes>` or not. If the pool is not homogeneous,
different authentication can be used for each connection acquired from the
pool.
.. attribute:: AsyncConnectionPool.increment
This read-only attribute returns the number of connections that will be
established when additional connections need to be created.
.. attribute:: AsyncConnectionPool.max
This read-only attribute returns the maximum number of connections that the
pool can control.
.. attribute:: AsyncConnectionPool.max_lifetime_session
This read-write attribute returns the maximum length of time (in seconds)
that a pooled connection may exist. Connections that are in use will not be
closed. They become candidates for termination only when they are released
back to the pool and have existed for longer than max_lifetime_session
seconds. Note that termination only occurs when the pool is accessed. A
value of 0 means that there is no maximum length of time that a pooled
connection may exist. This attribute is only available in Oracle Database
12.1 or later.
.. attribute:: AsyncConnectionPool.max_sessions_per_shard
This read-write attribute returns the number of sessions that can be
created per shard in the pool. This attribute cannot be used in
python-oracledb Thin mode.
.. attribute:: AsyncConnectionPool.min
This read-only attribute returns the number of connections with which the
connection pool was created and the minimum number of connections that will
be controlled by the connection pool.
.. attribute:: AsyncConnectionPool.name
This read-only attribute returns the name assigned to the pool by Oracle.
.. attribute:: AsyncConnectionPool.opened
This read-only attribute returns the number of connections currently opened
by the pool.
.. attribute:: AsyncConnectionPool.ping_interval
This read-write integer attribute specifies the pool ping interval in
seconds. When a connection is acquired from the pool, a check is first made
to see how long it has been since the connection was put into the pool. If
this idle time exceeds ``ping_interval``, then a :ref:`round-trip
<roundtrips>` ping to the database is performed. If the connection is
unusable, it is discarded and a different connection is selected to be
returned by :meth:`AsyncConnectionPool.acquire()`. Setting
``ping_interval`` to a negative value disables pinging. Setting it to 0
forces a ping for every :meth:`AsyncConnectionPool.acquire()` and is not
recommended.
Prior to cx_Oracle 8.2, the ping interval was fixed at 60 seconds.
.. attribute:: AsyncConnectionPool.soda_metadata_cache
This read-write boolean attribute returns whether the SODA metadata cache
is enabled or not. This attribute cannot be used in python-oracledb Thin
mode.
.. attribute:: AsyncConnectionPool.stmtcachesize
This read-write attribute specifies the size of the statement cache that
will be used for connections obtained from the pool. Once a connection is
created, that connections statement cache size can only be changed by
setting the stmtcachesize attribute on the connection itself.
See :ref:`Statement Caching <stmtcache>` for more information.
.. attribute:: AsyncConnectionPool.thin
This attribute returns a boolean which indicates the python-oracledb mode
in which the pool was created. If the value of this attribute is True, it
indicates that the pool was created in the python-oracledb Thin mode. If
the value of this attribute is False, it indicates that the pool was created
in the python-oracledb Thick mode.
.. attribute:: AsyncConnectionPool.timeout
This read-write attribute specifies the time (in seconds) after which idle
connections will be terminated in order to maintain an optimum number of
open connections. A value of 0 means that no idle connections are
terminated.
.. attribute:: AsyncConnectionPool.username
This read-only attribute returns the name of the user which established the
connection to the database.
.. attribute:: AsyncConnectionPool.wait_timeout
This read-write attribute specifies the time (in milliseconds) that the
caller should wait for a connection to become available in the pool before
returning with an error. This value is only used if the ``getmode``
parameter to :meth:`oracledb.create_pool_async()` was the value
:data:`oracledb.POOL_GETMODE_TIMEDWAIT`.

View File

@ -1,488 +0,0 @@
.. _asynccursorobj:
************************
API: AsyncCursor Objects
************************
An AsyncCursor object can be created with :meth:`AsyncConnection.cursor()`.
Unless explicitly noted as synchronous, the AsyncCursor methods should be used
with ``await``. This object is an extension to the DB API.
.. versionadded:: 2.0.0
.. note::
The Asynchronous I/O (asyncio) support in python-oracledb 2.0.0 is a
pre-release and may change in the next version.
.. note::
AsyncCursor objects are only supported in the python-oracledb Thin mode.
.. _asynccursormeth:
AsyncCursor Methods
===================
.. method:: AsyncCursor.__aiter__()
Returns the cursor itself to be used as an asynchronous iterator.
.. method:: AsyncCursor.__enter__()
The entry point for the cursor as a context manager. It returns itself.
.. method:: AsyncCursor.__exit__()
The exit point for the cursor as a context manager. It closes the cursor.
.. method:: AsyncCursor.arrayvar(typ, value, [size])
A synchronous method that creates an array variable associated with the
cursor of the given type and size and returns a
:ref:`variable object <varobj>`. The value is either an integer specifying
the number of elements to allocate or it is a list and the number of
elements allocated is drawn from the size of the list. If the value is a
list, the variable is also set with the contents of the list. If the size
is not specified and the type is a string or binary, 4000 bytes is
allocated. This is needed for passing arrays to PL/SQL (in cases where
the list might be empty and the type cannot be determined automatically) or
returning arrays from PL/SQL.
Array variables can only be used for PL/SQL associative arrays with
contiguous keys. For PL/SQL associative arrays with sparsely populated keys
or for varrays and nested tables, the approach shown in this
`example <https://github.com/oracle/python-oracledb/blob/main/
samples/plsql_collection.py>`__ needs to be used.
.. method:: AsyncCursor.bindnames()
A synchronous method that returns the list of bind variable names bound to
the statement. Note that a statement must have been prepared first.
.. method:: AsyncCursor.callfunc(name, returnType, parameters=[], \
keyword_parameters={})
Calls a function with the given name. The return type is specified in the
same notation as is required by :meth:`~AsyncCursor.setinputsizes()`. The
sequence of parameters must contain one entry for each parameter that the
function expects. Any keyword parameters will be included after the
positional parameters. The result of the call is the return value of the
function.
See :ref:`plsqlfunc` for an example.
.. note::
If you intend to call :meth:`AsyncCursor.setinputsizes()` on the cursor
prior to making this call, then note that the first item in the
parameter list refers to the return value of the function.
.. method:: AsyncCursor.callproc(name, parameters=[], keyword_parameters={})
Calls a procedure with the given name. The sequence of parameters must
contain one entry for each parameter that the procedure expects. The result
of the call is a modified copy of the input sequence. Input parameters are
left untouched; output and input/output parameters are replaced with
possibly new values. Keyword parameters will be included after the
positional parameters and are not returned as part of the output sequence.
See :ref:`plsqlproc` for an example.
.. method:: AsyncCursor.close()
A synchronous method that closes the cursor now, rather than whenever
``__del__`` is called. The cursor will be unusable from this point
forward; an Error exception will be raised if any operation is attempted
with the cursor.
.. method:: AsyncCursor.execute(statement, parameters=[], ** keyword_parameters)
Executes a statement against the database. See :ref:`sqlexecution`.
Parameters may be passed as a dictionary or sequence or as keyword
parameters. If the parameters are a dictionary, the values will be bound by
name and if the parameters are a sequence the values will be bound by
position. Note that if the values are bound by position, the order of the
variables is from left to right as they are encountered in the statement
and SQL statements are processed differently than PL/SQL statements. For
this reason, it is generally recommended to bind parameters by name instead
of by position.
Parameters passed as a dictionary are name and value pairs. The name maps
to the bind variable name used by the statement and the value maps to the
Python value you wish bound to that bind variable.
A reference to the statement will be retained by the cursor. If None or the
same string object is passed in again, the cursor will execute that
statement again without performing a prepare or rebinding and redefining.
This is most effective for algorithms where the same statement is used, but
different parameters are bound to it (many times). Note that parameters
that are not passed in during subsequent executions will retain the value
passed in during the last execution that contained them.
For maximum efficiency when reusing a statement, it is best to use the
:meth:`~AsyncCursor.setinputsizes()` method to specify the parameter types and
sizes ahead of time; in particular, None is assumed to be a string of
length 1 so any values that are later bound as numbers or dates will raise
a TypeError exception.
If the statement is a query, the cursor is returned as a convenience to the
caller (so it can be used directly as an iterator over the rows in the
cursor); otherwise, ``None`` is returned.
.. method:: AsyncCursor.executemany(statement, parameters, batcherrors=False, \
arraydmlrowcounts=False)
Prepares a statement for execution against a database and then execute it
against all parameter mappings or sequences found in the sequence
parameters. See :ref:`batchstmnt`.
The ``statement`` parameter is managed in the same way as the
:meth:`~AsyncCursor.execute()` method manages it. If the size of the buffers
allocated for any of the parameters exceeds 2 GB, you will receive the
error "DPI-1015: array size of <n> is too large", where <n> varies with the
size of each element being allocated in the buffer. If you receive this
error, decrease the number of elements in the sequence parameters.
If there are no parameters, or parameters have previously been bound, the
number of iterations can be specified as an integer instead of needing to
provide a list of empty mappings or sequences.
When True, the ``batcherrors`` parameter enables batch error support within
Oracle and ensures that the call succeeds even if an exception takes place
in one or more of the sequence of parameters. The errors can then be
retrieved using :meth:`~AsyncCursor.getbatcherrors()`.
When True, the ``arraydmlrowcounts`` parameter enables DML row counts to be
retrieved from Oracle after the method has completed. The row counts can
then be retrieved using :meth:`~AsyncCursor.getarraydmlrowcounts()`.
Both the ``batcherrors`` parameter and the ``arraydmlrowcounts`` parameter
can only be true when executing an insert, update, delete or merge
statement; in all other cases an error will be raised.
For maximum efficiency, it is best to use the
:meth:`~AsyncCursor.setinputsizes()` method to specify the parameter types and
sizes ahead of time; in particular, None is assumed to be a string of
length 1 so any values that are later bound as numbers or dates will raise
a TypeError exception.
.. method:: AsyncCursor.fetchall()
Fetches all (remaining) rows of a query result, returning them as a list of
tuples. An empty list is returned if no more rows are available. Note that
the cursor's ``arraysize`` attribute can affect the performance of this
operation, as internally reads from the database are done in batches
corresponding to ``arraysize``.
An exception is raised if the previous call to
:meth:`~AsyncCursor.execute()` did not produce any result set or no call
was issued yet.
.. method:: AsyncCursor.fetchmany(size=cursor.arraysize)
Fetches the next set of rows of a query result, returning a list of tuples.
An empty list is returned if no more rows are available. Note that the
cursor's arraysize attribute can affect the performance of this operation.
The number of rows to fetch is specified by the parameter. If it is not
given, the cursor's arraysize attribute determines the number of rows to be
fetched. If the number of rows available to be fetched is fewer than the
amount requested, fewer rows will be returned.
An exception is raised if the previous call to
:meth:`~AsyncCursor.execute()` did not produce any result set or no call
was issued yet.
.. method:: AsyncCursor.fetchone()
Fetches the next row of a query result set, returning a single tuple or
None when no more data is available.
An exception is raised if the previous call to
:meth:`~AsyncCursor.execute()` did not produce any result set or no call
was issued yet.
.. method:: AsyncCursor.getarraydmlrowcounts()
A synchronous method that retrieves the DML row counts after a call to
:meth:`~AsyncCursor.executemany()` with arraydmlrowcounts enabled. This
will return a list of integers corresponding to the number of rows
affected by the DML statement for each element of the array passed to
:meth:`~AsyncCursor.executemany()`.
.. note::
This method is only available for Oracle 12.1 and later.
.. method:: AsyncCursor.getbatcherrors()
A synchronous method that retrieves the exceptions that took place after a
call to :meth:`~AsyncCursor.executemany()` with batcherrors enabled. This
will return a list of Error objects, one error for each iteration that
failed. The offset can be determined by looking at the offset attribute of
the error object.
.. method:: AsyncCursor.getimplicitresults()
A synchronous method that returns a list of cursors which correspond to
implicit results made available from a PL/SQL block or procedure without
the use of OUT ref cursor parameters. The PL/SQL block or procedure opens
the cursors and marks them for return to the driver using the procedure
dbms_sql.return_result. Cursors returned in this fashion should not be
closed. They will be closed automatically by the parent cursor when it is
closed. Closing the parent cursor will invalidate the cursors returned by
this method.
.. note::
This method is only available with Oracle Database 12.1 or later. It is
most like the DB API method nextset(), but unlike that method (which
requires that the next result set overwrite the current result set),
this method returns cursors which can be fetched independently of each
other.
.. method:: AsyncCursor.parse(statement)
This can be used to parse a statement without actually executing it
(parsing step is done automatically by Oracle when a statement is
:meth:`executed <AsyncCursor.execute>`).
.. note::
You can parse any DML or DDL statement. DDL statements are executed
immediately and an implied commit takes place.
.. method:: AsyncCursor.prepare(statement, tag, cache_statement=True)
A synchronous method that can be used before a call to
:meth:`~AsyncCursor.execute()` to define the statement that will be
executed. When this is done, the prepare phase will not be performed when
the call to :meth:`~AsyncCursor.execute()` is made with None or the same
string object as the statement.
If the ``tag`` parameter is specified and the ``cache_statement`` parameter
is True, the statement will be returned to the statement cache with the
given tag.
If the ``cache_statement`` parameter is False, the statement will be
removed from the statement cache (if it was found there) or will simply not
be cached.
See :ref:`Statement Caching <stmtcache>` for more information.
.. method:: AsyncCursor.setinputsizes(*args, **keywordArgs)
A synchronous method that can be used before a call to
:meth:`~AsyncCursor.execute()`, :meth:`~AsyncCursor.executemany()`,
:meth:`~AsyncCursor.callfunc()` or :meth:`~AsyncCursor.callproc()` to
predefine memory areas for the operation's parameters. Each parameter
should be a type object corresponding to the input that will be used or it
should be an integer specifying the maximum length of a string parameter.
Use keyword parameters when binding by name and positional parameters when
binding by position. The singleton None can be used as a parameter when
using positional parameters to indicate that no space should be reserved
for that position.
.. note::
If you plan to use :meth:`~AsyncCursor.callfunc()` then be aware that the
first parameter in the list refers to the return value of the function.
.. method:: AsyncCursor.setoutputsize(size, [column])
This method does nothing and is retained solely for compatibility with the
DB API. The module automatically allocates as much space as needed to fetch
LONG and LONG RAW columns (or CLOB as string and BLOB as bytes).
.. method:: AsyncCursor.var(typ, [size, arraysize, inconverter, outconverter, \
typename, encoding_errors, bypass_decode, convert_nulls])
A synchronous method that creates a variable with the specified
characteristics. This method was designed for use with PL/SQL in/out
variables where the length or type cannot be determined automatically from
the Python object passed in or for use in input and output type handlers
defined on cursors or connections.
The ``typ`` parameter specifies the type of data that should be stored in the
variable. This should be one of the :ref:`database type constants
<dbtypes>`, :ref:`DB API constants <types>`, an object type returned from
the method :meth:`AsyncConnection.gettype()` or one of the following Python
types:
.. list-table-with-summary::
:header-rows: 1
:class: wy-table-responsive
:align: center
:summary: The first column is the Python Type. The second column is the corresponding Database Type.
* - Python Type
- Database Type
* - bool
- :attr:`oracledb.DB_TYPE_BOOLEAN`
* - bytes
- :attr:`oracledb.DB_TYPE_RAW`
* - datetime.date
- :attr:`oracledb.DB_TYPE_DATE`
* - datetime.datetime
- :attr:`oracledb.DB_TYPE_DATE`
* - datetime.timedelta
- :attr:`oracledb.DB_TYPE_INTERVAL_DS`
* - decimal.Decimal
- :attr:`oracledb.DB_TYPE_NUMBER`
* - float
- :attr:`oracledb.DB_TYPE_NUMBER`
* - int
- :attr:`oracledb.DB_TYPE_NUMBER`
* - str
- :attr:`oracledb.DB_TYPE_VARCHAR`
The ``size`` parameter specifies the length of string and raw variables and is
ignored in all other cases. If not specified for string and raw variables,
the value 4000 is used.
The ``arraysize`` parameter specifies the number of elements the variable will
have. If not specified the bind array size (usually 1) is used. When a
variable is created in an output type handler this parameter should be set
to the cursor's array size.
The ``inconverter`` and ``outconverter`` parameters specify methods used for
converting values to/from the database. More information can be found in
the section on :ref:`variable objects<varobj>`.
The ``typename`` parameter specifies the name of a SQL object type and must be
specified when using type :data:`oracledb.OBJECT` unless the type object
was passed directly as the first parameter.
The ``encoding_errors`` parameter specifies what should happen when decoding
byte strings fetched from the database into strings. It should be one of
the values noted in the builtin
`decode <https://docs.python.org/3/library/stdtypes.html#bytes.decode>`__
function.
The ``bypass_decode`` parameter, if specified, should be passed as a
boolean value. Passing a `True` value causes values of database types
:data:`~oracledb.DB_TYPE_VARCHAR`, :data:`~oracledb.DB_TYPE_CHAR`,
:data:`~oracledb.DB_TYPE_NVARCHAR`, :data:`~oracledb.DB_TYPE_NCHAR` and
:data:`~oracledb.DB_TYPE_LONG` to be returned as `bytes` instead of `str`,
meaning that python-oracledb does not do any decoding. See :ref:`Fetching raw
data <fetching-raw-data>` for more information.
The ``convert_nulls`` parameter, if specified, should be passed as a boolean
value. Passing the value ``True`` causes the ``outconverter`` to be called
when a null value is fetched from the database; otherwise, the
``outconverter`` is only called when non-null values are fetched from the
database.
.. _asynccursorattr:
AsyncCursor Attributes
======================
.. attribute:: AsyncCursor.arraysize
This read-write attribute can be used to tune the number of rows internally
fetched and buffered by internal calls to the database when fetching rows
from SELECT statements and REF CURSORS. The value can drastically affect
the performance of a query since it directly affects the number of network
round trips between Python and the database. For methods like
:meth:`~AsyncCursor.fetchone()` and :meth:`~AsyncCursor.fetchall()` it
does not change how many rows are returned to the application. For
:meth:`~AsyncCursor.fetchmany()` it is the default number of rows to fetch.
The attribute is only used for tuning row and SODA document fetches from
the database. It does not affect data inserts.
Due to the performance benefits, the default ``Cursor.arraysize`` is 100
instead of the 1 that the Python DB API recommends.
See :ref:`Tuning Fetch Performance <tuningfetch>` for more information.
.. attribute:: AsyncCursor.bindvars
This read-only attribute provides the bind variables used for the last
execute. The value will be either a list or a dictionary depending on
whether binding was done by position or name. Care should be taken when
referencing this attribute. In particular, elements should not be removed
or replaced.
.. attribute:: AsyncCursor.description
This read-only attribute is a sequence of :ref:`FetchInfo<fetchinfoobj>`
objects. This attribute will be None for operations that do not return rows
or if the cursor has not had an operation invoked via the
:meth:`~AsyncCursor.execute()` method yet.
.. attribute:: AsyncCursor.fetchvars
This read-only attribute specifies the list of variables created for the
last query that was executed on the cursor. Care should be taken when
referencing this attribute. In particular, elements should not be removed
or replaced.
.. attribute:: AsyncCursor.inputtypehandler
This read-write attribute specifies a method called for each value that is
bound to a statement executed on the cursor and overrides the attribute
with the same name on the connection if specified. The method signature is
handler(cursor, value, arraysize) and the return value is expected to be a
variable object or None in which case a default variable object will be
created. If this attribute is None, the default behavior will take place
for all values bound to the statements.
.. attribute:: AsyncCursor.lastrowid
This read-only attribute returns the rowid of the last row modified by the
cursor. If no row was modified by the last operation performed on the
cursor, the value None is returned.
.. attribute:: AsyncCursor.outputtypehandler
This read-write attribute specifies a method called for each column that is
to be fetched from this cursor. The method signature is
handler(cursor, metadata) and the return value is expected to be a
:ref:`variable object<varobj>` or None in which case a default variable
object will be created. If this attribute is None, then the default
behavior will take place for all columns fetched from this cursor.
See :ref:`outputtypehandlers`.
.. attribute:: AsyncCursor.prefetchrows
This read-write attribute can be used to tune the number of rows that the
python-oracledb fetches when a SELECT statement is executed. This value can
reduce the number of round-trips to the database that are required to fetch
rows but at the cost of additional memory. Setting this value to 0 can be
useful when the timing of fetches must be explicitly controlled.
The attribute is only used for tuning row fetches from the database. It
does not affect data inserts.
See :ref:`Tuning Fetch Performance <tuningfetch>` for more information.
.. attribute:: AsyncCursor.rowcount
This read-only attribute specifies the number of rows that have currently
been fetched from the cursor (for select statements), that have been
affected by the operation (for insert, update, delete and merge
statements), or the number of successful executions of the statement
(for PL/SQL statements).
.. attribute:: AsyncCursor.rowfactory
This read-write attribute specifies a method to call for each row that is
retrieved from the database. Ordinarily, a tuple is returned for each row
but if this attribute is set, the method is called with the tuple that
would normally be returned, and the result of the method is returned
instead.
See :ref:`rowfactories`.
.. attribute:: AsyncCursor.scrollable
This read-write boolean attribute specifies whether the cursor can be
scrolled or not. By default, cursors are not scrollable, as the server
resources and response times are greater than nonscrollable cursors. This
attribute is checked and the corresponding mode set in Oracle when calling
the method :meth:`~AsyncCursor.execute()`.

View File

@ -1,102 +0,0 @@
.. _asynclobobj:
*********************
API: AsyncLOB Objects
*********************
An AsyncLOB object can be created with :meth:`AsyncConnection.createlob()`.
Also, this object is returned whenever Oracle :data:`CLOB`, :data:`BLOB` and
:data:`BFILE` columns are fetched. This object is an extension to the DB API.
See :ref:`lobdata` for more information about using LOBs.
.. note::
The Asynchronous I/O (asyncio) support in python-oracledb 2.0.0 is a
pre-release and may change in the next version.
.. note::
AsyncLOB objects are only supported in the python-oracledb Thin mode.
.. _asynclobmeth:
AsyncLOB Methods
================
.. method:: AsyncLOB.close()
Closes the LOB. Call this when writing is completed so that the indexes
associated with the LOB can be updated -- but only if :meth:`~AsyncLOB.open()`
was called first.
.. method:: AsyncLOB.fileexists()
Returns a boolean indicating if the file referenced by the BFILE type LOB
exists.
.. method:: AsyncLOB.getchunksize()
Returns the chunk size for the internal LOB. Reading and writing to the LOB
in chunks of multiples of this size will improve performance.
.. method:: AsyncLOB.getfilename()
Returns a two-tuple consisting of the directory alias and file name for a
BFILE type LOB.
.. method:: AsyncLOB.isopen()
Returns a boolean indicating if the LOB has been opened using the method
:meth:`~AsyncLOB.open()`.
.. method:: AsyncLOB.open()
Opens the LOB for writing. This will improve performance when writing to a
LOB in chunks and there are functional or extensible indexes associated
with the LOB. If this method is not called, each write will perform an open
internally followed by a close after the write has been completed.
.. method:: AsyncLOB.read([offset=1, [amount]])
Returns a portion (or all) of the data in the LOB object. Note that the
amount and offset are in bytes for BLOB and BFILE type LOBs and in UCS-2
code points for CLOB and NCLOB type LOBs. UCS-2 code points are equivalent
to characters for all but supplemental characters. If supplemental
characters are in the LOB, the offset and amount will have to be chosen
carefully to avoid splitting a character.
.. method:: AsyncLOB.setfilename(dirAlias, name)
Sets the directory alias and name of the BFILE type LOB.
.. method:: AsyncLOB.size()
Returns the size of the data in the LOB object. For BLOB and BFILE type
LOBs, this is the number of bytes. For CLOB and NCLOB type LOBs, this is the
number of UCS-2 code points. UCS-2 code points are equivalent to characters
for all but supplemental characters.
.. method:: AsyncLOB.trim(new_size=0)
Trims the LOB to the new size.
.. method:: AsyncLOB.write(data, offset=1)
Writes the data to the LOB object at the given offset. The offset is in
bytes for BLOB type LOBs and in UCS-2 code points for CLOB and NCLOB type
LOBs. UCS-2 code points are equivalent to characters for all but
supplemental characters. If supplemental characters are in the LOB, the
offset will have to be chosen carefully to avoid splitting a character.
Note that if you want to make the LOB value smaller, you must use the
:meth:`~AsyncLOB.trim()` function.
.. _asynclobattr:
AsyncLOB Attributes
===================
.. attribute:: AsyncLOB.type
This read-only attribute returns the type of the LOB as one of the
:ref:`database type constants <dbtypes>`.

View File

@ -18,46 +18,31 @@ ConnectParams Methods
.. method:: ConnectParams.copy()
Creates a copy of the ConnectParams instance and returns it.
Creates a copy of the ConnectParams instance and returns it.
.. method:: ConnectParams.get_connect_string()
Returns the connection string associated with the ConnectParams instance.
Returns the connection string associated with the ConnectParams instance.
.. method:: ConnectParams.parse_connect_string(connect_string)
Parses the connect string into its components and stores the parameters.
Parses the connect string into its components and stores the parameters.
The ``connect string`` parameter can be an Easy Connect string, name-value
pairs, or a simple alias which is looked up in ``tnsnames.ora``. Parameters
that are found in the connect string override any currently stored values.
.. method:: ConnectParams.parse_dsn_with_credentials(dsn)
Parses a DSN in the form <user>/<password>@<connect_string> or in the form
<user>/<password> and returns a 3-tuple containing the parsed user,
password and connect string. Empty strings are returned as the value
``None``.
.. versionadded:: 1.3.0
The ``connect string`` parameter can be an Easy Connect string, name-value
pairs, or a simple alias which is looked up in ``tnsnames.ora``. Parameters
that are found in the connect string override any currently stored values.
.. method:: ConnectParams.set(user=None, proxy_user=None, password=None, \
newpassword=None, wallet_password=None, access_token=None, host=None, \
port=None, protocol=None, https_proxy=None, https_proxy_port=None, \
service_name=None, sid=None, server_type=None, cclass=None, \
purity=None, expire_time=None, retry_count=None, retry_delay=None, \
tcp_connect_timeout=None, ssl_server_dn_match=None, \
ssl_server_cert_dn=None, wallet_location=None, events=None, \
externalauth=None, mode=None, disable_oob=None, stmtcachesize=None, \
edition=None, tag=None, matchanytag=None, config_dir=None, \
appcontext=[], shardingkey=[], supershardingkey=[], debug_jdwp=None, \
connection_id_prefix=None, ssl_context=None, sdu=None, handle=None)
newpassword=None, wallet_password=None, access_token=None, host=None, \
port=None, protocol=None, https_proxy=None, https_proxy_port=None, service_name=None, \
sid=None, server_type=None, cclass=None, purity=None, expire_time=None, retry_count=None, \
retry_delay=None, tcp_connect_timeout=None, ssl_server_dn_match=None, \
ssl_server_cert_dn=None, wallet_location=None, events=None, externalauth=None, \
mode=None, disable_oob=None, stmtcachesize=None, edition=None, tag=None, \
matchanytag=None, config_dir=None, appcontext=[], shardingkey=[], supershardingkey=[], \
debug_jdwp=None, handle=None)
Sets the default values for one or more of the parameters of an empty
ConnectParams object. A default will be overriden when a connection string
with that attribute is parsed. After a ConnectParams object has been
populated by parsing a connection string, ``ConnectParams.set()`` will not
override any values.
Sets one or more of the parameters.
.. _connparamsattr:
@ -67,312 +52,276 @@ ConnectParams Attributes
.. attribute:: ConnectParams.appcontext
This read-only attribute is a list that specifies the application context
used by the connection. It is a list of 3-tuples that includes the
namespace, name, and value. Each entry in the tuple is a string.
This read-only attribute is a list that specifies the application context used by
the connection. It is a list of 3-tuples that includes the namespace, name, and value.
Each entry in the tuple is a string.
This attribute is only supported in the python-oracledb Thick mode.
This attribute is only supported in the python-oracledb Thick mode.
.. attribute:: ConnectParams.cclass
This read-only attribute is a string that specifies the connection class
to use for Database Resident Connection Pooling (DRCP).
This read-only attribute is a string that specifies the connection class
to use for Database Resident Connection Pooling (DRCP).
This attribute is supported in the python-oracledb Thin and Thick modes.
This attribute is supported in the python-oracledb Thin and Thick modes.
.. attribute:: ConnectParams.config_dir
This read-only attribute is a string that identifies the directory in which
the configuration files such as tnsnames.ora are found. The default is the
value of :attr:`defaults.config_dir`.
This read-only attribute is a string that identifies the directory in which
the configuration files such as tnsnames.ora are found. The default is the
value of :attr:`defaults.config_dir`.
This attribute is only supported in the python-oracledb Thin mode.
This attribute is only supported in the python-oracledb Thin mode.
For the python-oracledb Thick mode, use the ``config_dir`` parameter of
:func:`oracledb.init_oracle_client`.
.. attribute:: ConnectParams.connection_id_prefix
This read-only attribute is a string that is added to the beginning of the
generated ``connection_id`` that is sent to the database for
`tracing <https://www.oracle.com/pls/topic/lookup?
ctx=dblatest&id=GUID-B0FC69F9-2EBC-44E8-ACB2-62FBA14ABD5C>`__.
.. versionadded:: 1.4.0
For the python-oracledb Thick mode, use the ``config_dir`` parameter of
:func:`oracledb.init_oracle_client`.
.. attribute:: ConnectParams.debug_jdwp
This read-only attribute is a string with the format
"host=<host>;port=<port>" that specifies the host and port of the PL/SQL
debugger. This allows the Java Debug Wire Protocol (JDWP) to debug the
PL/SQL code invoked by the python-oracledb driver. The default value is the
value of the environment variable ``ORA_DEBUG_JDWP``.
This read-only attribute is a string with the format "host=<host>;port=<port>"
that specifies the host and port of the PL/SQL debugger. This allows the
Java Debug Wire Protocol (JDWP) to debug the PL/SQL code invoked by the
python-oracledb driver. The default value is the value of the environment
variable ``ORA_DEBUG_JDWP``.
This attribute is only supported in the python-oracledb Thin mode. For
the python-oracledb Thick mode, set the ``ORA_DEBUG_JDWP`` environment
variable which has the same syntax. See :ref:`applntracing` for more
information.
This attribute is only supported in the python-oracledb Thin mode. For
the python-oracledb Thick mode, set the ``ORA_DEBUG_JDWP`` environment
variable which has the same syntax. See :ref:`applntracing` for more
information.
.. attribute:: ConnectParams.disable_oob
This read-only attribute is a boolean that indicates whether out-of-band
breaks should be disabled. The default value is False. Note that this value
has no effect on Windows, which does not support this functionality.
This read-only attribute is a boolean that indicates whether out-of-band
breaks should be disabled. The default value is False. Note that this value
has no effect on Windows, which does not support this functionality.
This attribute is only supported in the python-oracledb Thin mode.
This attribute is only supported in the python-oracledb Thin mode.
For the python-oracledb Thick mode, set the equivalent option in a
``sqlnet.ora`` file.
For the python-oracledb Thick mode, set the equivalent option in a
``sqlnet.ora`` file.
.. attribute:: ConnectParams.edition
This read-only attribute is a string that specifies the edition to use
for the connection. This attribute cannot be used simultaneously with the
:attr:`ConnectParams.cclass` attribute.
This read-only attribute is a string that specifies the edition to use
for the connection. This attribute cannot be used simultaneously with the
:attr:`ConnectParams.cclass` attribute.
This attribute is supported in the python-oracledb Thin and Thick modes.
This attribute is supported in the python-oracledb Thin and Thick modes.
.. attribute:: ConnectParams.events
This read-only attribute is a boolean that specifies whether the events
mode should be enabled.
This read-only attribute is a boolean that specifies whether the events mode
should be enabled.
This attribute is needed for continuous query notification (CQN) and high
availability event notifications. The default value is False.
This attribute is needed for continuous query notification (CQN) and high
availability event notifications. The default value is False.
This attribute is only supported in the python-oracledb Thick mode.
This attribute is only supported in the python-oracledb Thick mode.
.. attribute:: ConnectParams.expire_time
This read-only attribute is an integer that returns the number of minutes
between the sending of keepalive probes.
This read-only attribute is an integer that returns the number of minutes
between the sending of keepalive probes.
The default value is 0. If this attribute is set to a value greater than zero,
it enables keepalive.
The default value is 0. If this attribute is set to a value greater than
zero, it enables keepalive.
This attribute is supported in the python-oracledb Thin and Thick modes.
This attribute is supported in the python-oracledb Thin and Thick modes.
.. attribute:: ConnectParams.externalauth
This read-only attribute is a boolean that specifies whether external
authentication should be used. The default value is False.
This read-only attribute is a boolean that specifies whether external
authentication should be used. The default value is False.
For standalone connections, external authentication occurs when the
``user`` and ``password`` attributes are not used. If these attributes,
are not used, you can optionally set the ``externalauth`` attribute to
True, which may aid code auditing.
For standalone connections, external authentication occurs when the
``user`` and ``password`` attributes are not used. If these attributes,
are not used, you can optionally set the ``externalauth`` attribute to
True, which may aid code auditing.
This attribute is only supported in the python-oracledb Thick mode.
This attribute is only supported in the python-oracledb Thick mode.
.. attribute:: ConnectParams.host
This read-only attribute is a string that returns the name or IP address of
the machine hosting the database.
This read-only attribute is a string that returns the name or IP address of
the machine hosting the database.
This attribute is supported in the python-oracledb Thin and Thick modes.
This attribute is supported in the python-oracledb Thin and Thick modes.
.. attribute:: ConnectParams.https_proxy
This read-only attribute is a string that returns the name or IP address of
a proxy host that is to be used for tunneling secure connections.
This read-only attribute is a string that returns the name or IP address of
a proxy host that is to be used for tunneling secure connections.
This attribute is supported in the python-oracledb Thin and Thick modes.
This attribute is supported in the python-oracledb Thin and Thick modes.
.. attribute:: ConnectParams.https_proxy_port
This read-only attribute is an integer that returns the port to be used to
communicate with the proxy host. The default value is 0.
This read-only attribute is an integer that returns the port to be used to
communicate with the proxy host.
The default value is 0.
This attribute is supported in the python-oracledb Thin and Thick modes.
This attribute is supported in the python-oracledb Thin and Thick modes.
.. attribute:: ConnectParams.matchanytag
This read-only attribute is a boolean that specifies whether any tag can be
used when acquiring a connection from the pool. The default value is False.
This read-only attribute is a boolean that specifies whether any tag can be
used when acquiring a connection from the pool.
The default value is False.
This attribute is only supported in the python-oracledb Thick mode.
This attribute is only supported in the python-oracledb Thick mode.
.. attribute:: ConnectParams.mode
This read-only attribute is an integer that specifies the authorization mode
to use. The default value is :data:`~oracledb.AUTH_MODE_DEFAULT`.
This read-only attribute is an integer that specifies the authorization mode
to use.
The default value is :data:`~oracledb.AUTH_MODE_DEFAULT`.
This attribute is supported in the python-oracledb Thin and Thick modes.
This attribute is supported in the python-oracledb Thin and Thick modes.
.. attribute:: ConnectParams.port
This read-only attribute is an integer that returns the port number on
which the database listener is listening. The default value is 1521.
This read-only attribute is an integer that returns the port number on which
the database listener is listening. The default value is 1521.
This attribute is supported in the python-oracledb Thin and Thick modes.
This attribute is supported in the python-oracledb Thin and Thick modes.
.. attribute:: ConnectParams.protocol
This read-only attribute is a string that indicates whether unencrypted
network traffic or encrypted network traffic (TLS) is used and it can have
the value "tcp" or "tcps". The default value is "tcp".
This read-only attribute is a string that indicates whether unencrypted network
traffic or encrypted network traffic (TLS) is used and it can have the value
tcp or tcps.
The default value is tcp.
This attribute is supported in the python-oracledb Thin and Thick modes.
This attribute is supported in the python-oracledb Thin and Thick modes.
.. attribute:: ConnectParams.proxy_user
This read-only attribute is a string that specifies the name of the proxy
user to connect to. If this value is not specified, then it will be parsed
out of the user if the user attribute is in the form "user[proxy_user]".
This read-only attribute is a string that specifies the name of the proxy user to connect to.
If this value is not specified, then it will be parsed out of the user if the user attribute
is in the form "user[proxy_user]".
This attribute is supported in the python-oracledb Thin and Thick modes.
This attribute is supported in the python-oracledb Thin and Thick modes.
.. attribute:: ConnectParams.purity
This read-only attribute is an integer that returns the purity used for
DRCP. When the value of this attribute is :attr:`oracledb.PURITY_DEFAULT`,
then any standalone connection will use :attr:`oracledb.PURITY_NEW` and any
pooled connection will use :attr:`oracledb.PURITY_SELF`. The default value
is :data:`~oracledb.PURITY_DEFAULT`.
This read-only attribute is an integer that returns the purity used for DRCP.
When the value of this attribute is :attr:`oracledb.PURITY_DEFAULT`, then any
standalone connection will use :attr:`oracledb.PURITY_NEW` and any pooled
connection will use :attr:`oracledb.PURITY_SELF`. The default value is
:data:`~oracledb.PURITY_DEFAULT`.
This attribute is supported in the python-oracledb Thin and Thick modes.
This attribute is supported in the python-oracledb Thin and Thick modes.
.. attribute:: ConnectParams.retry_count
This read-only attribute is an integer that returns the number of times
that a connection attempt should be retried before the attempt is
terminated. The default value is 0.
This read-only attribute is an integer that returns the number of times that a
connection attempt should be retried before the attempt is terminated.
The default value is 0.
This attribute is supported in the python-oracledb Thin and Thick modes.
This attribute is supported in the python-oracledb Thin and Thick modes.
.. attribute:: ConnectParams.retry_delay
This read-only attribute is an integer that returns the number of seconds
to wait before making a new connection attempt. The default value is 0.
This read-only attribute is an integer that returns the number of seconds to
wait before making a new connection attempt.
The default value is 0.
This attribute is supported in the python-oracledb Thin and Thick modes.
.. attribute:: ConnectParams.sdu
This read-only attribute is an integer that returns the requested size of
the Session Data Unit (SDU), in bytes. The value tunes internal buffers
used for communication to the database. Bigger values can increase
throughput for large queries or bulk data loads, but at the cost of higher
memory use. The SDU size that will actually be used is negotiated down to
the lower of this value and the database network SDU configuration value.
See the `SQL*Net documentation
<https://www.oracle.com/pls/topic/lookup?ctx=dblatest&
id=GUID-86D61D6F-AD26-421A-BABA-77949C8A2B04>`__ for more details.
This attribute is supported in the python-oracledb Thin and Thick modes.
.. versionadded:: 2.0.0
This attribute is supported in the python-oracledb Thin and Thick modes.
.. attribute:: ConnectParams.server_type
This read-only attribute is a string that returns the type of server
connection that should be established. If specified, it should be one of
`dedicated`, `shared`, or `pooled`.
This read-only attribute is a string that returns the type of server connection
that should be established. If specified, it should be one of `dedicated`, `shared`,
or `pooled`.
This attribute is supported in the python-oracledb Thin and Thick modes.
This attribute is supported in the python-oracledb Thin and Thick modes.
.. attribute:: ConnectParams.service_name
This read-only attribute is a string that returns the service name of the
database.
This read-only attribute is a string that returns the service name of the database.
This attribute is supported in the python-oracledb Thin and Thick modes.
This attribute is supported in the python-oracledb Thin and Thick modes.
.. attribute:: ConnectParams.shardingkey
This read-only attribute is a list that specifies a sequence of strings,
numbers, bytes, or dates that identify the database shard to connect to.
This read-only attribute is a list that specifies a sequence of strings, numbers,
bytes, or dates that identify the database shard to connect to.
This attribute is only supported in the python-oracledb Thick mode.
This attribute is only supported in the python-oracledb Thick mode.
.. attribute:: ConnectParams.sid
This read-only attribute is a string that returns the SID of the database.
It is recommended to use the :attr:`ConnectParams.service_name` instead.
This read-only attribute is a string that returns the SID of the database.
It is recommended to use the :attr:`ConnectParams.service_name` instead.
This attribute is supported in the python-oracledb Thin and Thick modes.
.. attribute:: ConnectParams.ssl_context
This read-only attribute is an `SSLContext object
<https://docs.python.org/3/library/ssl.html#ssl-contexts>`__ which is used
for connecting to the database using TLS. This SSL context will be modified
to include the private key or any certificates found in a separately
supplied wallet. This parameter should only be specified if the default
SSLContext object cannot be used.
This attribute is only supported in the python-oracledb Thin mode.
.. versionadded:: 2.0.0
This attribute is supported in the python-oracledb Thin and Thick modes.
.. attribute:: ConnectParams.ssl_server_cert_dn
This read-only attribute is a string that returns the distinguished name
(DN), which should be matched with the server. If this value is specified,
then it is used for any verification. Otherwise, the hostname will be used.
This read-only attribute is a string that returns the distinguished name (DN),
which should be matched with the server. If this value is specified, then it is
used for any verification. Otherwise, the hostname will be used.
This value is ignored if the :attr:`~ConnectParams.ssl_server_dn_match`
attribute is not set to the value `True`.
This value is ignored if the :attr:`~ConnectParams.ssl_server_dn_match`
attribute is not set to the value `True`.
This attribute is supported in the python-oracledb Thin and Thick modes.
This attribute is supported in the python-oracledb Thin and Thick modes.
.. attribute:: ConnectParams.ssl_server_dn_match
This read-only attribute is a boolean that indicates whether the server
certificate distinguished name (DN) should be matched in addition to the
regular certificate verification that is performed. The default value is
True.
This read-only attribute is a boolean that indicates whether the server certificate
distinguished name (DN) should be matched in addition to the regular
certificate verification that is performed. The default value is True.
Note that if the :attr:`~ConnectParams.ssl_server_cert_dn` attribute is not
specified, then host name matching is performed instead.
Note that if the :attr:`~ConnectParams.ssl_server_cert_dn` attribute is not specified,
then host name matching is performed instead.
This attribute is supported in the python-oracledb Thin and Thick modes.
This attribute is supported in the python-oracledb Thin and Thick modes.
.. attribute:: ConnectParams.stmtcachesize
This read-only attribute is an integer that identifies the initial size of
the statement cache. The default is the value of
:attr:`defaults.stmtcachesize`.
This read-only attribute is an integer that identifies the initial size of
the statement cache. The default is the value of
:attr:`defaults.stmtcachesize`.
This attribute is supported in the python-oracledb Thin and Thick modes.
This attribute is supported in the python-oracledb Thin and Thick modes.
.. attribute:: ConnectParams.supershardingkey
This read-only attribute is a list that specifies a sequence of strings,
numbers, bytes, or dates that identify the database shard to connect to.
This read-only attribute is a list that specifies a sequence of strings, numbers,
bytes, or dates that identify the database shard to connect to.
This attribute is only supported in python-oracledb Thick mode.
This attribute is only supported in python-oracledb Thick mode.
.. attribute:: ConnectParams.tag
This read-only attribute is a string that identifies the type of connection
that should be returned from a pool.
This read-only attribute is a string that identifies the type of connection that
should be returned from a pool.
This attribute is only supported in python-oracledb Thick mode.
This attribute is only supported in python-oracledb Thick mode.
.. attribute:: ConnectParams.tcp_connect_timeout
This read-only attribute is a float that indicates the maximum number of
seconds to wait for a connection to be established to the database host.
The default value is 60.0.
This read-only attribute is a float that indicates the maximum number of seconds
to wait for a connection to be established to the database host.
The default value is 60.0.
This attribute is supported in the python-oracledb Thin and Thick modes.
This attribute is supported in the python-oracledb Thin and Thick modes.
.. attribute:: ConnectParams.user
This read-only attribute is a string that specifies the name of the user to
connect to.
This read-only attribute is a string that specifies the name of the user to
connect to.
This attribute is supported in the python-oracledb Thin and Thick modes.
This attribute is supported in the python-oracledb Thin and Thick modes.
.. attribute:: ConnectParams.wallet_location
This read-only attribute is a string that specifies the directory where the
wallet can be found.
This read-only attribute is a string that specifies the directory where the
wallet can be found.
In python-oracledb Thin mode, this attribute is the directory containing
the PEM-encoded wallet file, ewallet.pem. In python-oracledb Thick mode,
this attribute is the directory containing the file, cwallet.sso.
In python-oracledb Thin mode, this attribute is the directory containing the
PEM-encoded wallet file, ewallet.pem. In python-oracledb Thick mode, this
attribute is the directory containing the file, cwallet.sso.
This attribute is supported in the python-oracledb Thin and Thick modes.
This attribute is supported in the python-oracledb Thin and Thick modes.

View File

@ -4,8 +4,7 @@
API: Connection Objects
***********************
A Connection object can be created with :meth:`oracledb.connect()` or with
:meth:`ConnectionPool.acquire()`.
A Connection object can be created with :meth:`oracledb.connect()`.
.. note::
@ -24,6 +23,7 @@ Connection Methods
This method is an extension to the DB API definition.
.. method:: Connection.__exit__()
The exit point for the connection as a context manager. This will close
@ -33,6 +33,7 @@ Connection Methods
This method is an extension to the DB API definition.
.. method:: Connection.begin([formatId, transactionId, branchId])
Explicitly begins a new transaction. Without parameters, this explicitly
@ -60,6 +61,7 @@ Connection Methods
This method is an extension to the DB API definition.
.. method:: Connection.changepassword(oldpassword, newpassword)
Changes the password for the user to which the connection is
@ -88,19 +90,11 @@ Connection Methods
Commits any pending transactions to the database.
.. method:: Connection.createlob(lob_type, data=None)
.. method:: Connection.createlob(lobType)
Creates and returns a new temporary :ref:`LOB object <lobobj>` of the
specified type. The ``lob_type`` parameter should be one of
:data:`oracledb.DB_TYPE_CLOB`, :data:`oracledb.DB_TYPE_BLOB`, or
:data:`oracledb.DB_TYPE_NCLOB`.
If data is supplied, it will be written to the temporary LOB before it is
returned.
.. versionchanged:: 2.0
The parameter ``data`` was added.
specified type. The ``lobType`` parameter should be one of
:data:`oracledb.CLOB`, :data:`oracledb.BLOB` or :data:`oracledb.NCLOB`.
.. note::
@ -123,6 +117,7 @@ Connection Methods
This method is an extension to the DB API definition.
.. method:: Connection.gettype(name)
Returns a :ref:`type object <dbobjecttype>` given its name. This can then be
@ -133,6 +128,7 @@ Connection Methods
This method is an extension to the DB API definition.
.. method:: Connection.is_healthy()
This function returns a boolean indicating the health status of a connection.
@ -171,6 +167,7 @@ Connection Methods
This method is an extension to the DB API definition.
.. method:: Connection.prepare()
Prepares the distributed (global) transaction for commit. Return a boolean
@ -185,6 +182,7 @@ Connection Methods
This method is an extension to the DB API definition.
.. method:: Connection.queue(name, payload_type=None)
Creates a :ref:`queue <queue>` which is used to enqueue and dequeue
@ -206,10 +204,12 @@ Connection Methods
This method is an extension to the DB API definition.
.. method:: Connection.rollback()
Rolls back any pending transactions.
.. method:: Connection.shutdown([mode])
Shuts down the database. In order to do this the connection must be connected
@ -352,7 +352,7 @@ Connection Methods
explicitly closed using the function :meth:`~Connection.close()`, the
subscription will not be deregistered in the database.
.. method:: Connection.tpc_begin(xid, flags, timeout)
.. method:: Connection.tpc_begin(xid, flags)
Begins a Two-Phase Commit (TPC) on a global transaction using the specified
transaction identifier (xid).
@ -364,19 +364,10 @@ Connection Methods
:data:`oracledb.TPC_BEGIN_NEW`, :data:`oracledb.TPC_BEGIN_PROMOTE`, or
:data:`oracledb.TPC_BEGIN_RESUME`. The default is :data:`oracledb.TPC_BEGIN_NEW`.
The ``timeout`` parameter is the number of seconds to wait for a transaction to
become available for resumption when :data:`~oracledb.TPC_BEGIN_RESUME` is
specified in the ``flags`` parameter. When :data:`~oracledb.TPC_BEGIN_NEW` is
specified in the ``flags`` parameter, the ``timeout`` parameter indicates the
number of seconds the transaction can be inactive before it is automatically
terminated by the system. A transaction is inactive between the time it is
detached with :meth:`Connection.tpc_end()` and the time it is resumed with
:meth:`Connection.tpc_begin()`.The default is 0 seconds.
The following code sample demonstrates the ``tpc_begin()`` function::
connection.tpc_begin(xid=x, flags=oracledb.TPC_BEGIN_NEW)
x = connection.xid(format_id=1, global_transaction_id="tx1", branch_qualifier="br1")
connection.tpc_begin(xid=x, flags=oracledb.TPC_BEGIN_NEW, timeout=30)
See :ref:`tcp` for information on TPC.
@ -401,8 +392,8 @@ Connection Methods
The following code sample demonstrates the ``tpc_commit()`` function::
x = connection.xid(format_id=1, global_transaction_id="tx1", branch_qualifier="br1")
connection.tpc_commit(xid=x, one_phase=False)
x = connection.xid(format_id=1, global_transaction_id="tx1", branch_qualifier="br1")
See :ref:`tcp` for information on TPC.
@ -424,8 +415,8 @@ Connection Methods
The following code sample demonstrates the ``tpc_end()`` function::
x = connection.xid(format_id=1, global_transaction_id="tx1", branch_qualifier="br1")
connection.tpc_end(xid=x, flags=oracledb.TPC_END_NORMAL)
x = connection.xid(format_id=1, global_transaction_id="tx1", branch_qualifier="br1")
See :ref:`tcp` for information on TPC.
@ -439,8 +430,8 @@ Connection Methods
The following code sample demonstrates the ``tpc_forget()`` function::
x = connection.xid(format_id=1, global_transaction_id="tx1", branch_qualifier="br1")
connection.tpc_forget(xid=x)
x = connection.xid(format_id=1, global_transaction_id="tx1", branch_qualifier="br1")
See :ref:`tcp` for information on TPC.
@ -461,8 +452,8 @@ Connection Methods
The following code sample demonstrates the ``tpc_prepare()`` function::
x = connection.xid(format_id=1, global_transaction_id="tx1", branch_qualifier="br1")
connection.tpc_prepare(xid=x)
x = connection.xid(format_id=1, global_transaction_id="tx1", branch_qualifier="br1")
See :ref:`tcp` for information on TPC.
@ -496,8 +487,8 @@ Connection Methods
The following code sample demonstrates the ``tpc_rollback()`` function::
x = connection.xid(format_id=1, global_transaction_id="tx1", branch_qualifier="br1")
connection.tpc_rollback(xid=x)
x = connection.xid(format_id=1, global_transaction_id="tx1", branch_qualifier="br1")
See :ref:`tcp` for information on TPC.
@ -540,13 +531,14 @@ Connection Attributes
.. attribute:: Connection.action
This write-only attribute sets the action column in the v$session table. It
is a string attribute but the value None is accepted and treated as an
empty string.
is a string attribute and cannot be set to None -- use the empty string
instead.
.. note::
This attribute is an extension to the DB API definition.
.. attribute:: Connection.autocommit
This read-write attribute determines whether autocommit mode is on or off.
@ -586,6 +578,7 @@ Connection Attributes
This attribute is an extension to the DB API definition.
.. attribute:: Connection.clientinfo
This write-only attribute sets the client_info column in the v$session
@ -607,35 +600,11 @@ Connection Attributes
This attribute is an extension to the DB API definition.
.. attribute:: Connection.db_domain
This read-only attribute specifies the Oracle Database domain name
associated with the connection. It is the same value returned by the SQL
``SELECT value FROM V$PARAMETER WHERE NAME = 'db_domain'``.
.. versionadded:: 2.0.0
.. note::
This attribute is an extension to the DB API definition.
.. attribute:: Connection.db_name
This read-only attribute specifies the Oracle Database name associated with
the connection. It is the same value returned by the SQL
``SELECT NAME FROM V$DATABASE``.
.. versionadded:: 2.0.0
.. note::
This attribute is an extension to the DB API definition.
.. attribute:: Connection.dbop
This write-only attribute sets the database operation that is to be
monitored. This can be viewed in the ``DBOP_NAME`` column of the
``v$sql_monitor`` table.
``V$SQL_MONITOR`` table.
.. note::
@ -666,6 +635,18 @@ Connection Attributes
This attribute is an extension to the DB API definition.
.. attribute:: Connection.encoding
This read-only attribute returns the IANA character set name of the
character set in use by the Oracle client for regular strings. The
encodings in use are always UTF-8.
.. deprecated:: cx_Oracle 8.2
.. note::
This attribute is an extension to the DB API definition.
.. attribute:: Connection.external_name
This read-write attribute specifies the external name that is used by the
@ -677,9 +658,9 @@ Connection Attributes
.. attribute:: Connection.handle
This read-only attribute returns the Oracle Call Interface (OCI) service
context handle for the connection. It is primarily provided to facilitate
testing the creation of a connection using the OCI service context handle.
This read-only attribute returns the Oracle Call Interface (OCI) service context handle for the
connection. It is primarily provided to facilitate testing the creation of
a connection using the OCI service context handle.
This property is only relevant in the python-oracledb Thick mode.
@ -687,6 +668,7 @@ Connection Attributes
This attribute is an extension to the DB API definition.
.. attribute:: Connection.inputtypehandler
This read-write attribute specifies a method called for each value that is
@ -700,17 +682,6 @@ Connection Attributes
This attribute is an extension to the DB API definition.
.. attribute:: Connection.instance_name
This read-only attribute specifies the Oracle Database instance name
associated with the connection. It is the same value as the SQL expression
``sys_context('userenv', 'instance_name')``.
.. versionadded:: 1.4.0
.. note::
This attribute is an extension to the DB API definition.
.. attribute:: Connection.internal_name
@ -735,18 +706,21 @@ Connection Attributes
server and the client.
.. attribute:: Connection.max_open_cursors
.. attribute:: Connection.maxBytesPerCharacter
This read-only attribute specifies the maximum number of cursors that the
database can have open concurrently. It is the same value returned by the
SQL ``SELECT VALUE FROM V$PARAMETER WHERE NAME = 'open_cursors'``.
This deprecated, read-only attribute returns the value 4 since encodings
are always UTF-8.
.. versionadded:: 2.0.0
Previously it returned the maximum number of bytes each character can use
for the client character set.
.. deprecated:: cx_Oracle 8.2
.. note::
This attribute is an extension to the DB API definition.
.. attribute:: Connection.module
This write-only attribute sets the module column in the v$session table.
@ -757,61 +731,29 @@ Connection Attributes
This attribute is an extension to the DB API definition.
.. attribute:: Connection.nencoding
This read-only attribute returns the IANA character set name of the
national character set in use by the Oracle client. This is always the value "UTF-8".
.. deprecated:: cx_Oracle 8.2
.. note::
This attribute is an extension to the DB API definition.
.. attribute:: Connection.outputtypehandler
This read-write attribute specifies a method called for each column that is
going to be fetched from any cursor associated with this connection. The
method signature is ``handler(cursor, metadata)`` and the return value is
expected to be a :ref:`variable object<varobj>` or None in which case a
default variable object will be created. If this attribute is None, the
default behavior will take place for all columns fetched from cursors.
method signature is handler(cursor, name, defaultType, length, precision,
scale) and the return value is expected to be a variable object or None in
which case a default variable object will be created. If this attribute is
None, the default behavior will take place for all columns fetched from
cursors.
See :ref:`outputtypehandlers`.
.. versionchanged:: 1.4
The method signature was changed. The previous signature
``handler(cursor, name, default_type, length, precision, scale)`` will
still work but is deprecated and will be removed in a future version.
.. note::
This attribute is an extension to the DB API definition.
.. attribute:: Connection.proxy_user
This read-only attribute returns the name of the user which was used as a
proxy when creating the connection to the database.
.. versionadded:: 2.0.0
.. note::
This attribute is an extension to the DB API definition.
.. attribute:: Connection.sdu
This read-only attribute specifies the size of the Session Data Unit (SDU)
that is being used by the connection. The value will be the lesser of the
requested python-oracledb size and the maximum size allowed by the database
network configuration. It is available only in the python-oracledb Thin
mode.
.. versionadded:: 2.0.0
.. note::
This attribute is an extension to the DB API definition.
.. attribute:: Connection.service_name
This read-only attribute specifies the Oracle Database service name
associated with the connection. This is the same value returned by the SQL
``SELECT SYS_CONTEXT('USERENV', 'SERVICE_NAME') FROM DUAL``.
.. versionadded:: 2.0.0
.. note::
This attribute is an extension to the DB API definition.
@ -856,12 +798,14 @@ Connection Attributes
This attribute is an extension to the DB API definition.
.. attribute:: Connection.transaction_in_progress
.. attribute:: Connection.tnsentry
This read-only attribute specifies whether a transaction is currently in
progress on the database associated with the connection.
This read-only attribute returns the TNS entry of the database to which a
connection has been established.
.. versionadded:: 2.0.0
.. deprecated:: cx_Oracle 8.2
Use the attribute :attr:`~Connection.dsn` instead.
.. note::
@ -887,37 +831,6 @@ Connection Attributes
.. note::
If you connect to Oracle Database 18 or higher using Oracle Client
libraries 12.2 or lower you will only receive the base version (such as
18.0.0.0.0) instead of the full version (such as 18.3.0.0.0).
.. attribute:: Connection.warning
This read-only attribute provides an :ref:`oracledb._Error<exchandling>`
object giving information about any database warnings (such as the password
being in the grace period, or the pool being created with a smaller than
requested size due to database resource restrictions) that were generated
during connection establishment or by :meth:`oracledb.create_pool()`. The
attribute will be present if there was a warning, but creation otherwise
completed successfully. The connection will be usable despite the warning.
For :ref:`standalone connections <standaloneconnection>`,
``Connection.warning`` will be present for the lifetime of the connection.
For :ref:`pooled connections <connpooling>`, ``Connection.warning`` will be
cleared when a connection is released to the pool such as with
:meth:`ConnectionPool.release()`.
In python-oracledb Thick mode, warnings may be generated during pool
creation itself. These warnings will be placed on new connections created
by the pool, provided no warnings were generated by the individual
connection creations, in which case those connection warnings will be
returned.
If no warning was generated the value ``None`` is returned.
.. versionadded:: 2.0.0
.. note::
This attribute is an extension to the DB API definition.
If you connect to Oracle Database 18 or higher with client libraries
12.2 or lower that you will only receive the base version (such as
18.0.0.0.0) instead of the full version (18.3.0.0.0).

View File

@ -77,6 +77,7 @@ ConnectionPool Methods
database shard to connect to. The key values can be strings, numbers, bytes
or dates.
.. method:: ConnectionPool.close(force=False)
Closes the pool now, rather than when the last reference to it is
@ -85,11 +86,13 @@ ConnectionPool Methods
If any connections have been acquired and not released back to the pool,
this method will fail unless the ``force`` parameter is set to True.
.. method:: ConnectionPool.drop(connection)
Drops the connection from the pool which is useful if the connection is no
longer usable (such as when the session is killed).
.. method:: ConnectionPool.reconfigure([min, max, increment, getmode, \
timeout, wait_timeout, max_lifetime_session, max_sessions_per_shard, \
soda_metadata_cache, stmtcachesize, ping_interval])
@ -142,6 +145,7 @@ ConnectionPool Methods
See :ref:`Connection Pool Reconfiguration <poolreconfiguration>`.
.. method:: ConnectionPool.release(connection, tag=None)
Releases the connection back to the pool now, rather than whenever __del__
@ -164,6 +168,7 @@ ConnectionPool Methods
parameter are not None, the connection will be retagged when it is released
back to the pool.
ConnectionPool Attributes
=========================
@ -172,11 +177,13 @@ ConnectionPool Attributes
This read-only attribute returns the number of connections currently
acquired.
.. attribute:: ConnectionPool.dsn
This read-only attribute returns the TNS entry of the database to which a
connection has been established.
.. attribute:: ConnectionPool.getmode
This read-write attribute determines how connections are returned from the
@ -192,21 +199,24 @@ ConnectionPool Attributes
.. attribute:: ConnectionPool.homogeneous
This read-only boolean attribute indicates whether the pool is considered
This read-write boolean attribute indicates whether the pool is considered
:ref:`homogeneous <connpooltypes>` or not. If the pool is not homogeneous,
different authentication can be used for each connection acquired from the
pool.
.. attribute:: ConnectionPool.increment
This read-only attribute returns the number of connections that will be
established when additional connections need to be created.
.. attribute:: ConnectionPool.max
This read-only attribute returns the maximum number of connections that the
pool can control.
.. attribute:: ConnectionPool.max_lifetime_session
This read-write attribute returns the maximum length of time (in seconds)
@ -216,7 +226,7 @@ ConnectionPool Attributes
seconds. Note that termination only occurs when the pool is accessed. A
value of 0 means that there is no maximum length of time that a pooled
connection may exist. This attribute is only available in Oracle Database
12.1 or later.
12.1.
.. attribute:: ConnectionPool.max_sessions_per_shard
@ -228,21 +238,25 @@ ConnectionPool Attributes
of sessions for each shard. This attribute is only available in Oracle
Client 18.3 and higher.
.. attribute:: ConnectionPool.min
This read-only attribute returns the number of connections with which the
connection pool was created and the minimum number of connections that will
be controlled by the connection pool.
.. attribute:: ConnectionPool.name
This read-only attribute returns the name assigned to the pool by Oracle.
.. attribute:: ConnectionPool.opened
This read-only attribute returns the number of connections currently opened
by the pool.
.. attribute:: ConnectionPool.ping_interval
This read-write integer attribute specifies the pool ping interval in
@ -276,6 +290,7 @@ ConnectionPool Attributes
See :ref:`Statement Caching <stmtcache>` for more information.
.. attribute:: ConnectionPool.thin
This attribute returns a boolean which indicates the python-oracledb mode
@ -292,12 +307,22 @@ ConnectionPool Attributes
terminated. Note that in python-oracledb Thick mode with older Oracle
Client Libraries, the termination only occurs when the pool is accessed.
.. attribute:: ConnectionPool.tnsentry
This read-only attribute returns the TNS entry of the database to which a
connection has been established.
.. deprecated:: cx_Oracle 8.2
Use the attribute :attr:`~ConnectionPool.dsn` instead.
.. attribute:: ConnectionPool.username
This read-only attribute returns the name of the user which established the
connection to the database.
.. attribute:: ConnectionPool.wait_timeout
This read-write attribute specifies the time (in milliseconds) that the

View File

@ -4,7 +4,7 @@
API: Cursor Objects
*******************
A cursor object can be created with :meth:`Connection.cursor()`.
A cursor object can be created with :meth:`Connection.cursor`.
Cursor Methods
==============
@ -26,14 +26,6 @@ Cursor Methods
This method is an extension to the DB API definition.
.. method:: Cursor.__iter__()
Returns the cursor itself to be used as an iterator.
.. note::
This method is an extension to the DB API definition but it is
mentioned in PEP 249 as an optional extension.
.. method:: Cursor.arrayvar(typ, value, [size])
@ -57,6 +49,7 @@ Cursor Methods
The DB API definition does not define this method.
.. method:: Cursor.bindnames()
Returns the list of bind variable names bound to the statement. Note that a
@ -93,6 +86,7 @@ Cursor Methods
prior to making this call, then note that the first item in the
parameter list refers to the return value of the function.
.. method:: Cursor.callproc(name, parameters=[], keyword_parameters={})
Calls a procedure with the given name. The sequence of parameters must
@ -144,7 +138,7 @@ Cursor Methods
that are not passed in during subsequent executions will retain the value
passed in during the last execution that contained them.
For maximum efficiency when reusing a statement, it is best to use the
For maximum efficiency when reusing an statement, it is best to use the
:meth:`~Cursor.setinputsizes()` method to specify the parameter types and
sizes ahead of time; in particular, None is assumed to be a string of
length 1 so any values that are later bound as numbers or dates will raise
@ -158,6 +152,7 @@ Cursor Methods
The DB API definition does not define the return value of this method.
.. method:: Cursor.executemany(statement, parameters, batcherrors=False, \
arraydmlrowcounts=False)
@ -195,6 +190,7 @@ Cursor Methods
length 1 so any values that are later bound as numbers or dates will raise
a TypeError exception.
.. method:: Cursor.fetchall()
Fetches all (remaining) rows of a query result, returning them as a list of
@ -208,6 +204,7 @@ Cursor Methods
See :ref:`fetching` for an example.
.. method:: Cursor.fetchmany(size=cursor.arraysize)
Fetches the next set of rows of a query result, returning a list of tuples.
@ -234,6 +231,7 @@ Cursor Methods
See :ref:`fetching` for an example.
.. method:: Cursor.getarraydmlrowcounts()
Retrieves the DML row counts after a call to :meth:`~Cursor.executemany()`
@ -244,7 +242,8 @@ Cursor Methods
.. note::
The DB API definition does not define this method and it is only
available for Oracle 12.1 and later.
available for Oracle 12.1 and higher.
.. method:: Cursor.getbatcherrors()
@ -257,6 +256,7 @@ Cursor Methods
The DB API definition does not define this method.
.. method:: Cursor.getimplicitresults()
Returns a list of cursors which correspond to implicit results made
@ -277,11 +277,21 @@ Cursor Methods
the current result set), this method returns cursors which can be
fetched independently of each other.
.. method:: Cursor.__iter__()
Returns the cursor itself to be used as an iterator.
.. note::
This method is an extension to the DB API definition but it is
mentioned in PEP 249 as an optional extension.
.. method:: Cursor.parse(statement)
This can be used to parse a statement without actually executing it
(parsing step is done automatically by Oracle when a statement is
:meth:`executed <Cursor.execute>`).
This can be used to parse a statement without actually executing it (this
step is done automatically by Oracle when a statement is executed).
.. note::
@ -292,6 +302,7 @@ Cursor Methods
You can parse any DML or DDL statement. DDL statements are executed
immediately and an implied commit takes place.
.. method:: Cursor.prepare(statement, tag, cache_statement=True)
This can be used before a call to :meth:`~Cursor.execute()` to define the
@ -313,6 +324,7 @@ Cursor Methods
The DB API definition does not define this method.
.. method:: Cursor.scroll(value=0, mode="relative")
Scrolls the cursor in the result set to a new position according to the
@ -332,31 +344,34 @@ Cursor Methods
This method is an extension to the DB API definition but it is
mentioned in PEP 249 as an optional extension.
.. method:: Cursor.setinputsizes(*args, **keywordArgs)
.. method:: Cursor.setinputsizes(\*args, \*\*keywordArgs)
This can be used before a call to :meth:`~Cursor.execute()`,
:meth:`~Cursor.executemany()`, :meth:`~Cursor.callfunc()` or
:meth:`~Cursor.callproc()` to predefine memory areas for the operation's
parameters. Each parameter should be a type object corresponding to the
input that will be used or it should be an integer specifying the maximum
length of a string parameter. Use keyword parameters when binding by name
and positional parameters when binding by position. The singleton None can
be used as a parameter when using positional parameters to indicate that no
space should be reserved for that position.
:meth:`~Cursor.callfunc()` or :meth:`~Cursor.callproc()` to predefine
memory areas for the operation's parameters. Each parameter should be a
type object corresponding to the input that will be used or it should be an
integer specifying the maximum length of a string parameter. Use keyword
parameters when binding by name and positional parameters when binding by
position. The singleton None can be used as a parameter when using
positional parameters to indicate that no space should be reserved for that
position.
.. note::
If you plan to use :meth:`~Cursor.callfunc()` then be aware that the
first parameter in the list refers to the return value of the function.
.. method:: Cursor.setoutputsize(size, [column])
This method does nothing and is retained solely for compatibility with the
DB API. The module automatically allocates as much space as needed to fetch
LONG and LONG RAW columns (or CLOB as string and BLOB as bytes).
.. method:: Cursor.var(typ, [size, arraysize, inconverter, outconverter, \
typename, encoding_errors, bypass_decode, convert_nulls])
typename, encoding_errors, bypass_decode])
Creates a variable with the specified characteristics. This method was
designed for use with PL/SQL in/out variables where the length or type
@ -427,20 +442,10 @@ Cursor Methods
meaning that python-oracledb does not do any decoding. See :ref:`Fetching raw
data <fetching-raw-data>` for more information.
The ``convert_nulls`` parameter, if specified, should be passed as a boolean
value. Passing the value ``True`` causes the ``outconverter`` to be called
when a null value is fetched from the database; otherwise, the
``outconverter`` is only called when non-null values are fetched from the
database.
For consistency and compliance with the PEP 8 naming style, the
parameter `encodingErrors` was renamed to `encoding_errors`. The old
name will continue to work as a keyword parameter for a period of time.
.. versionchanged:: 1.4.0
The ``convert_nulls`` parameter was added.
.. note::
The DB API definition does not define this method.
@ -489,18 +494,16 @@ Cursor Attributes
This attribute is an extension to the DB API definition but it is
mentioned in PEP 249 as an optional extension.
.. attribute:: Cursor.description
.. data:: Cursor.description
This read-only attribute is a sequence of :ref:`FetchInfo<fetchinfoobj>`
objects. This attribute will be None for operations that do not return rows
or if the cursor has not had an operation invoked via the
:meth:`~Cursor.execute()` method yet.
This read-only attribute is a sequence of 7-item sequences. Each of these
sequences contains information describing one result column: (name, type,
display_size, internal_size, precision, scale, null_ok). This attribute
will be None for operations that do not return rows or if the cursor has
not had an operation invoked via the :meth:`~Cursor.execute()` method yet.
.. versionchanged:: 1.4.0
Previously, this attribute was a sequence of 7-item sequences. Each
of these sequences contained information describing one result column:
(name, type, display_size, internal_size, precision, scale, null_ok).
The type will be one of the :ref:`database type constants <dbtypes>`
defined at the module level.
.. attribute:: Cursor.fetchvars
@ -527,29 +530,24 @@ Cursor Attributes
This attribute is an extension to the DB API definition.
.. attribute:: Cursor.lastrowid
.. data:: Cursor.lastrowid
This read-only attribute returns the rowid of the last row modified by the
cursor. If no row was modified by the last operation performed on the
cursor, the value None is returned.
.. attribute:: Cursor.outputtypehandler
This read-write attribute specifies a method called for each column that is
to be fetched from this cursor. The method signature is
handler(cursor, metadata) and the return value is expected to be a
:ref:`variable object<varobj>` or None in which case a default variable
object will be created. If this attribute is None, then the default
handler(cursor, name, defaultType, length, precision, scale) and the return
value is expected to be a variable object or None in which case a default
variable object will be created. If this attribute is None, then the default
behavior will take place for all columns fetched from this cursor.
See :ref:`outputtypehandlers`.
.. versionchanged:: 1.4.0
The method signature was changed. The previous signature
handler(cursor, name, default_type, length, precision, scale) will
still work but is deprecated and will be removed in a future version.
.. note::
This attribute is an extension to the DB API definition.
@ -565,9 +563,6 @@ Cursor Attributes
The attribute is only used for tuning row fetches from the database. It
does not affect data inserts.
Queries that return LOBs and similar types will never prefetch rows, so the
``prefetchrows`` value is ignored in those cases.
See :ref:`Tuning Fetch Performance <tuningfetch>` for more information.
.. note::
@ -582,6 +577,7 @@ Cursor Attributes
statements), or the number of successful executions of the statement
(for PL/SQL statements).
.. attribute:: Cursor.rowfactory
This read-write attribute specifies a method to call for each row that is
@ -617,21 +613,3 @@ Cursor Attributes
.. note::
The DB API definition does not define this attribute.
.. attribute:: Cursor.warning
This read-only attribute provides an :ref:`oracledb._Error<exchandling>`
object giving information about any database warnings (such as PL/SQL
compilation warnings) that were generated during the last call to
:meth:`~Cursor.execute()` or :meth:`~Cursor.executemany()`. This value is
automatically cleared on the next call to :meth:`~Cursor.execute()` or
:meth:`~Cursor.executemany()`. If no warning was generated the value
``None`` is returned.
See :ref:`plsqlwarning` for more information.
.. versionadded:: 2.0.0
.. note::
The DB API definition does not define this attribute.

View File

@ -39,8 +39,7 @@ Defaults Attributes
.. attribute:: defaults.fetch_decimals
Identifies whether numbers should be fetched as `decimal.Decimal
<https://docs.python.org/3/library/decimal.html#decimal-objects>`__ values.
Identifies whether numbers should be fetched as ``decimal.Decimal`` values.
This can help avoid issues with converting numbers from Oracle Database's
decimal format to Python's binary format.
@ -48,7 +47,7 @@ Defaults Attributes
`return_numbers_as_decimals.py <https://github.com/oracle/python-cx_Oracle/
blob/main/samples/return_numbers_as_decimals.py>`__) can alternatively be
used to adjust the returned type. If a type handler exists and returns a
variable (that is, ``cursor.var(...)``), then that return variable is used.
variable (that is, `cursor.var(...)`), then that return variable is used.
If the type handler returns None, then the value of
``oracledb.defaults.fetch_decimals`` is used to determine whether to return
``decimal.Decimal`` values.
@ -57,11 +56,10 @@ Defaults Attributes
.. attribute:: defaults.fetch_lobs
When the value of this attribute is True, then queries to LOB columns
return LOB locators. When the value of this attribute is False, then CLOBs
and NCLOBs are fetched as strings, and BLOBs are fetched as bytes. If LOBs
are larger than 1 GB, then this attribute should be set to True and the
LOBs should be streamed. See :ref:`lobdata`.
When the value of this attribute is True, then queries to LOB columns return
LOB locators. When the value of this attribute is False, then strings or bytes
are fetched. If LOBs are larger than 1 GB, then this attribute should be set to
True and the LOBs should be streamed. See :ref:`lobdata`.
An output type handler such as the one previously required in cx_Oracle (see
`return_lobs_as_strings.py <https://github.com/oracle/python-cx_Oracle/blob/main/samples/
@ -70,9 +68,6 @@ Defaults Attributes
that return variable is used. If the type handler returns None, then the value of
``oracledb.defaults.fetch_lobs`` is used.
The value of ``oracledb.defaults.fetch_lobs`` does not affect LOBs returned
as OUT binds.
This attribute has an initial value of True.
.. attribute:: defaults.prefetchrows

View File

@ -8,78 +8,6 @@ The following tables contain all of the deprecations in the python-oracledb API,
when they were first deprecated and a comment on what should be used instead,
if applicable. The most recent deprecations are listed first.
.. list-table-with-summary:: Desupported in python-oracledb 2.0
:header-rows: 1
:class: wy-table-responsive
:summary: The first column, Name, displays the desupported feature. The second column, Comments, includes information about the desupport and what replacement to make, if applicable.
:name: _desupported_2_0
* - Name
- Comments
* - ``oracledb.__future__.old_json_col_as_obj``
- VARCHAR2 and LOB columns created with the ``IS JSON`` check constraint
are now always fetched as JSON. Use an :ref:`output type handler
<outputtypehandlers>` if the old behavior is required.
* - Parameters ``encoding`` and ``nencoding`` of :func:`oracledb.connect()`
and :func:`oracledb.create_pool()`, and the related attributes on the
objects created
- The driver encodings are always UTF-8. Remove uses of ``encoding`` and
``nencoding`` from your code.
* - Parameter ``threaded`` of :func:`oracledb.connect()` and
:func:`oracledb.create_pool()`
- Threading is always used. Remove uses of ``threaded`` from your code.
* - Parameter ``waitTimeout`` of :func:`oracledb.create_pool()` and
``oracledb.SessionPool()``
- Replace with parameter ``wait_timeout``
* - Parameter ``maxLifetimeSession`` of :func:`oracledb.create_pool()` and
``oracledb.SessionPool()``
- Replace with parameter ``max_lifetime_session``
* - Parameter ``sessionCallback`` of :func:`oracledb.create_pool()` and
``oracledb.SessionPool()``
- Replace with parameter ``session_callback``
* - Parameter ``maxSessionsPerShard`` of :func:`oracledb.create_pool()` and
``oracledb.SessionPool()``
- Replace with parameter ``max_sessions_per_shard``
* - Attribute ``maxBytesPerCharacter`` of the :ref:`connection object
<connobj>`
- The driver encodings are always UTF-8 so this attribute can be replaced by
the constant value 4
* - ``Connection.tnsentry``
- Replace with :attr:`Connection.dsn`
* - ``SessionPool.tnsentry``
- Replace with :attr:`ConnectionPool.dsn`
.. list-table-with-summary:: Deprecated in python-oracledb 2.0
:header-rows: 1
:class: wy-table-responsive
:summary: The first column, Name, displays the deprecated feature. The second column, Comments, includes information about the deprecatation and what replacement to use, if applicable.
:name: _deprecations_2_0
* - Name
- Comments
* - Calling :meth:`Variable.setvalue()` with a string value when the
variable type is one of :data:`oracledb.DB_TYPE_BLOB`,
:data:`oracledb.DB_TYPE_CLOB` or :data:`oracledb.DB_TYPE_NCLOB`.
- Call :meth:`Connection.createlob()` with the value instead and pass the
result to :meth:`Variable.setvalue()`.
* - Setting an attribute of type :data:`oracledb.DB_TYPE_BLOB`,
:data:`oracledb.DB_TYPE_CLOB` or :data:`oracledb.DB_TYPE_NCLOB` on a
database object to a string value.
- Call :meth:`Connection.createlob()` with the value instead and set the
attribute with the result.
.. list-table-with-summary:: Deprecated in python-oracledb 1.4
:header-rows: 1
:class: wy-table-responsive
:summary: The first column, Name, displays the deprecated feature. The second column, Comments, includes information about the deprecatation and what replacement to use, if applicable.
:name: _deprecations_1_4
* - Name
- Comments
* - Output type handler with arguments
``handler(cursor, name, default_type, length, precision, scale)``
- Replace with ``handler(cursor, metadata)``. See
:ref:`outputtypehandlers`.
.. list-table-with-summary:: Deprecated in python-oracledb 1.0
:header-rows: 1
@ -182,7 +110,9 @@ python-oracledb are listed below:
.. list-table-with-summary:: Deprecated in cx_Oracle 8.2
:header-rows: 1
:class: wy-table-responsive
:summary: The first column, Name, displays the deprecated feature. The second column, Comments, includes information about the deprecatation and what replacement to use, if applicable.
:summary: The first column, Name, displays the deprecated API name. The second column,
Comments, includes information about when the API was deprecated and what API to use,
if applicable.
:name: _deprecations_8_2
* - Name
@ -220,7 +150,7 @@ python-oracledb are listed below:
* - ``maxSessionsPerShard`` parameter to `cx_Oracle.SessionPool() <https://cx-oracle.readthedocs.io/en/latest/api_manual/module.html#cx_Oracle.SessionPool>`_
- Replace with parameter name ``max_sessions_per_shard``
* - ``SessionPool.tnsentry``
- Replace with :attr:`ConnectionPool.dsn`
- Replace with `SessionPool.dsn <https://cx-oracle.readthedocs.io/en/latest/api_manual/session_pool.html#SessionPool.dsn>`_
* - ``payloadType`` parameter to `Connection.queue() <https://cx-oracle.readthedocs.io/en/latest/api_manual/connection.html#Connection.queue>`_
- Replace with parameter name ``payload_type`` if using keyword parameters.
* - ``ipAddress`` parameter to `Connection.subscribe() <https://cx-oracle.readthedocs.io/en/latest/api_manual/connection.html#Connection.subscribe>`_
@ -236,7 +166,7 @@ python-oracledb are listed below:
* - ``Connection.callTimeout``
- Replace with `Connection.call_timeout <https://cx-oracle.readthedocs.io/en/latest/api_manual/connection.html#Connection.call_timeout>`_
* - ``Connection.tnsentry``
- Replace with :attr:`Connection.dsn`
- Replace with `Connection.dsn <https://cx-oracle.readthedocs.io/en/latest/api_manual/connection.html#Connection.dsn>`_
* - `keywordParameters` parameter to `Cursor.callfunc() <https://cx-oracle.readthedocs.io/en/latest/api_manual/cursor.html#Cursor.callfunc>`_
- Replace with parameter name ``keyword_parameters``
* - ``keywordParameters`` parameter to `Cursor.callproc() <https://cx-oracle.readthedocs.io/en/latest/api_manual/cursor.html#Cursor.callproc>`_
@ -278,7 +208,7 @@ python-oracledb are listed below:
.. list-table-with-summary:: Deprecated in cx_Oracle 8.0
:header-rows: 1
:class: wy-table-responsive
:summary: The first column, Name, displays the deprecated feature. The second column, Comments, includes information about the deprecatation and what replacement to use, if applicable.
:summary: The first column, Name, displays the deprecated API name. The second column, Comments, includes information about when the API was deprecated and what API to use, if applicable.
:name: _deprecations_8_0
* - Name
@ -320,7 +250,7 @@ python-oracledb are listed below:
.. list-table-with-summary:: Deprecated in cx_Oracle 7.2
:header-rows: 1
:class: wy-table-responsive
:summary: The first column, Name, displays the deprecated feature. The second column, Comments, includes information about the deprecatation and what replacement to use, if applicable.
:summary: The first column, Name, displays the deprecated API name. The second column, Comments, includes information about when the API was deprecated and what API to use, if applicable.
:name: _deprecations_7_2
* - Name
@ -338,7 +268,7 @@ python-oracledb are listed below:
.. list-table-with-summary:: Deprecated in cx_Oracle 6.4
:header-rows: 1
:class: wy-table-responsive
:summary: The first column, Name, displays the deprecated feature. The second column, Comments, includes information about the deprecatation and what replacement to use, if applicable.
:summary: The first column, Name, displays the deprecated API name. The second column, Comments, includes information about when the API was deprecated and what API to use, if applicable.
:name: _deprecations_6_4
* - Name

View File

@ -1,106 +0,0 @@
.. _fetchinfoobj:
**********************
API: FetchInfo Objects
**********************
FetchInfo objects are created internally when a query is executed. They are found
in the sequence :data:`Cursor.description`. There is one FetchInfo object for
each column. For compatibility with the Python Database API, this object
behaves as a 7-tuple containing the values for the attributes ``name``,
``type_code``, ``display_size``, ``internal_size``, ``precision``, ``scale``,
and ``null_ok`` in that order. For example, if ``fetch_info`` is of type
FetchInfo, then ``fetch_info[2]`` is the same as ``fetch_info.display_size``.
.. versionadded:: 1.4.0
.. note::
This object is an extension the DB API.
FetchInfo Attributes
====================
.. attribute:: FetchInfo.annotations
This read-only attribute returns a dictionary containing the `annotations
<https://docs.oracle.com/en/database/oracle/oracle-database/23/sqlrf/
annotations_clause.html#GUID-1AC16117-BBB6-4435-8794-2B99F8F68052>`__
associated with the fetched column. If there are no annotations, the value
``None`` is returned. Annotations require Oracle Database 23c. If using
python-oracledb Thick mode, Oracle Client 23c is also required.
.. versionadded:: 2.0.0
.. attribute:: FetchInfo.display_size
This read-only attribute returns the display size of the column as mandated
by the Python Database API.
.. attribute:: FetchInfo.domain_name
This read-only attribute returns the name of the `SQL domain
<https://docs.oracle.com/en/database/oracle/oracle-database/23/sqlrf/
create-domain.html#GUID-17D3A9C6-D993-4E94-BF6B-CACA56581F41>`__
associated with the fetched column. If there is no SQL domain, the value
``None`` is returned. SQL domains require Oracle Database 23c. If using
python-oracledb Thick mode, Oracle Client 23c is also required.
.. versionadded:: 2.0.0
.. attribute:: FetchInfo.domain_schema
This read-only attribute returns the schema of the `SQL domain
<https://docs.oracle.com/en/database/oracle/oracle-database/23/sqlrf/
create-domain.html#GUID-17D3A9C6-D993-4E94-BF6B-CACA56581F41>`__
associated with the fetched column. If there is no SQL domain, the value
``None`` is returned. SQL domains require Oracle Database 23c. If using
python-oracledb Thick mode, Oracle Client 23c is also required.
.. versionadded:: 2.0.0
.. attribute:: FetchInfo.internal_size
This read-only attribute returns the internal size of the column as
mandated by the Python Database API.
.. attribute:: FetchInfo.is_json
This read-only attribute returns whether the column is known to contain
JSON data. This will be ``True`` when the type code is
``oracledb.DB_TYPE_JSON`` as well as when an "IS JSON" constraint is
enabled on LOB and VARCHAR2 columns.
.. attribute:: FetchInfo.name
This read-only attribute returns the name of the column as mandated by the
Python Database API.
.. attribute:: FetchInfo.null_ok
This read-only attribute returns whether nulls are allowed in the column as
mandated by the Python Database API.
.. attribute:: FetchInfo.precision
This read-only attribute returns the precision of the column as mandated by
the Python Database API.
.. attribute:: FetchInfo.scale
This read-only attribute returns the scale of the column as mandated by
the Python Database API.
.. attribute:: FetchInfo.type
This read-only attribute returns the type of the column. This will be an
:ref:`Oracle Object Type <dbobjecttype>` if the column contains Oracle
objects; otherwise, it will be one of the :ref:`database type constants
<dbtypes>` defined at the module level.
.. attribute:: FetchInfo.type_code
This read-only attribute returns the type of the column as mandated by the
Python Database API. The type will be one of the :ref:`database type
constants <dbtypes>` defined at the module level.

View File

@ -4,8 +4,7 @@
API: LOB Objects
****************
A LOB object can be created with :meth:`Connection.createlob()`. See
:ref:`lobdata` for more information about using LOBs.
See :ref:`lobdata` for more information about using LOBs.
.. note::
@ -21,26 +20,31 @@ LOB Methods
associated with the LOB can be updated -- but only if :meth:`~LOB.open()`
was called first.
.. method:: LOB.fileexists()
Returns a boolean indicating if the file referenced by the BFILE type LOB
exists.
.. method:: LOB.getchunksize()
Returns the chunk size for the internal LOB. Reading and writing to the LOB
in chunks of multiples of this size will improve performance.
.. method:: LOB.getfilename()
Returns a two-tuple consisting of the directory alias and file name for a
BFILE type LOB.
.. method:: LOB.isopen()
Returns a boolean indicating if the LOB has been opened using the method
:meth:`~LOB.open()`.
.. method:: LOB.open()
Opens the LOB for writing. This will improve performance when writing to a
@ -48,6 +52,7 @@ LOB Methods
with the LOB. If this method is not called, each write will perform an open
internally followed by a close after the write has been completed.
.. method:: LOB.read([offset=1, [amount]])
Returns a portion (or all) of the data in the LOB object. Note that the
@ -57,10 +62,12 @@ LOB Methods
characters are in the LOB, the offset and amount will have to be chosen
carefully to avoid splitting a character.
.. method:: LOB.setfilename(dirAlias, name)
Sets the directory alias and name of the BFILE type LOB.
.. method:: LOB.size()
Returns the size of the data in the LOB object. For BLOB and BFILE type
@ -68,10 +75,12 @@ LOB Methods
number of UCS-2 code points. UCS-2 code points are equivalent to characters
for all but supplemental characters.
.. method:: LOB.trim(new_size=0)
Trims the LOB to the new size.
.. method:: LOB.write(data, offset=1)
Writes the data to the LOB object at the given offset. The offset is in
@ -83,7 +92,7 @@ LOB Methods
:meth:`~LOB.trim()` function.
LOB Attributes
==============
===============
.. attribute:: LOB.type

File diff suppressed because it is too large Load Diff

View File

@ -32,21 +32,18 @@ PoolParams Methods
which is looked up in ``tnsnames.ora``. Parameters that are found in the connect string
override any currently stored values.
.. method:: PoolParams.set(min=None, max=None, increment=None, \
connectiontype=None, getmode=None, homogeneous=None, timeout=None, \
wait_timeout=None, max_lifetime_session=None, session_callback=None, \
max_sessions_per_shard=None, soda_metadata_cache=None, \
ping_interval=None, user=None, proxy_user=None, password=None, \
newpassword=None, wallet_password=None, access_token=None, host=None, \
port=None, protocol=None, https_proxy=None, https_proxy_port=None, \
service_name=None, sid=None, server_type=None, cclass=None, \
purity=None, expire_time=None, retry_count=None, retry_delay=None, \
tcp_connect_timeout=None, ssl_server_dn_match=None, \
ssl_server_cert_dn=None, wallet_location=None, events=None, \
externalauth=None, mode=None, disable_oob=None, stmtcachesize=None, \
edition=None, tag=None, matchanytag=None, config_dir=None, \
appcontext=[], shardingkey=[], supershardingkey=[], debug_jdwp=None, \
connection_id_prefix=None, ssl_context=None, sdu=None, handle=None)
.. method:: PoolParams.set(min=None, max=None, increment=None, connectiontype=None, \
getmode=None, homogeneous=None, timeout=None, wait_timeout=None, \
max_lifetime_session=None, session_callback=None, max_sessions_per_shard=None, \
soda_metadata_cache=None, ping_interval=None, user=None, proxy_user=None,\
password=None, newpassword=None, wallet_password=None, access_token=None, \
host=None, port=None, protocol=None, https_proxy=None, https_proxy_port=None, \
service_name=None, sid=None, server_type=None, cclass=None, purity=None, \
expire_time=None, retry_count=None, retry_delay=None, tcp_connect_timeout=None, \
ssl_server_dn_match=None, ssl_server_cert_dn=None, wallet_location=None, \
events=None, externalauth=None, mode=None, disable_oob=None, stmtcachesize=None, \
edition=None, tag=None, matchanytag=None, config_dir=None, appcontext=[], \
shardingkey=[], supershardingkey=[], debug_jdwp=None, handle=None)
Sets one or more of the parameters.
@ -58,20 +55,19 @@ PoolParams Attributes
.. attribute:: PoolParams.connectiontype
This read-only attribute specifies the class of the connection that should
be returned during calls to :meth:`ConnectionPool.acquire()`. It must be
Connection or a subclass of Connection. This attribute is of type
Type["oracledb.connection"]. The default value is ``oracledb.Connection``.
be returned during calls to :meth:`ConnectionPool.acquire()`. It must be Connection
or a subclass of Connection. This attribute is of type Type["oracledb.connection"].
The default value is ``oracledb.Connection``.
This attribute is supported in the python-oracledb Thin and Thick modes.
.. attribute:: PoolParams.getmode
This read-write attribute is an integer that determines the behavior of
:meth:`ConnectionPool.acquire()`. The value of this attribute can be one of
the constants :data:`oracledb.POOL_GETMODE_WAIT`,
:data:`oracledb.POOL_GETMODE_NOWAIT`, :data:`oracledb.POOL_GETMODE_FORCEGET`,
or :data:`oracledb.POOL_GETMODE_TIMEDWAIT`. The default value is
:data:`oracledb.POOL_GETMODE_WAIT`.
:meth:`ConnectionPool.acquire()`. The value of this attribute can be one of the
constants :data:`oracledb.POOL_GETMODE_WAIT`, :data:`oracledb.POOL_GETMODE_NOWAIT`,
:data:`oracledb.POOL_GETMODE_FORCEGET`, or :data:`oracledb.POOL_GETMODE_TIMEDWAIT`.
The default value is :data:`oracledb.POOL_GETMODE_WAIT`.
This attribute is supported in the python-oracledb Thin and Thick modes.
@ -129,10 +125,9 @@ PoolParams Attributes
(in seconds) after which an unused connection in the pool will be a
candidate for pinging when :meth:`ConnectionPool.acquire()` is called.
If the ping to the database indicates that the connection is not alive,
then a replacement connection will be returned by
:meth:`ConnectionPool.acquire()`. If the ``ping_interval`` is a negative
value, then the ping functionality will be disabled. The default value is 60
seconds.
then a replacement connection will be returned by :meth:`ConnectionPool.acquire()`.
If the ``ping_interval`` is a negative value, then the ping functionality
will be disabled. The default value is 60 seconds.
This attribute is supported in the python-oracledb Thin and Thick modes.

View File

@ -217,6 +217,7 @@ SodaCollection Methods
index has been created with the 'dataguide' option enabled. If there are
no documents in the collection, None is returned.
.. method:: SodaCollection.insertMany(docs)
Inserts a list of documents into the collection at one time. Each of the
@ -286,14 +287,6 @@ SodaCollection Methods
Use of the ``hint`` parameter requires Oracle Client 21.3 or higher (or Oracle Client 19 from 19.11).
.. method:: SodaCollection.listIndexes()
Returns a list of specifications for the indexes found on the collection.
This method requires Oracle Client 21.3 or later (or Oracle Client 19 from
19.13).
.. versionadded:: 1.4.0
.. method:: SodaCollection.save(doc)
@ -544,6 +537,7 @@ SodaOperation Methods
Use of this method requires Oracle Client 21.3 or higher (or Oracle Client
19 from 19.11).
.. method:: SodaOperation.key(value)
Specifies that the document with the specified key should be returned.
@ -576,29 +570,6 @@ SodaOperation Methods
criteria can be specified by chaining methods together.
.. method:: SodaOperation.lock()
Specifies whether the documents fetched from the collection should be
locked (equivalent to SQL "select for update").
The next commit or rollback on the connection made after the operation is
performed will "unlock" the documents. Ensure that the connection is not in
autocommit mode or the documents will be unlocked immediately after the
operation is complete.
This method should only be used with read operations (other than
:func:`~SodaOperation.count()`) and should not be used in
conjunction with non-terminal methods :meth:`~SodaOperation.skip()` and
:meth:`~SodaOperation.limit()`.
If this method is specified in conjunction with a write operation this
method is ignored.
This method is only supported in Oracle Client 21.3 or later (or
Oracle Client 19 from 19.11).
.. versionadded:: 1.4.0
.. method:: SodaOperation.remove()
Removes all of the documents in the collection that match the criteria. The

View File

@ -136,9 +136,8 @@ Message Objects
This read-only attribute returns a list of message query objects that give
information about query result sets changed for this notification. This
attribute will be an empty list if the ``qos`` parameter did not include
the flag :data:`~oracledb.SUBSCR_QOS_QUERY` when the subscription was
created.
attribute will be None if the ``qos`` parameter did not include the flag
:data:`~oracledb.SUBSCR_QOS_QUERY` when the subscription was created.
.. attribute:: Message.queue_name
@ -171,7 +170,7 @@ Message Objects
This read-only attribute returns a list of message table objects that give
information about the tables changed for this notification. This
attribute will be an empty list if the ``qos`` parameter included the flag
attribute will be None if the ``qos`` parameter included the flag
:data:`~oracledb.SUBSCR_QOS_QUERY` when the subscription was created.

View File

@ -50,13 +50,6 @@ Variable Attributes
name will continue to work for a period of time.
.. attribute:: Variable.convert_nulls
This read-only attribute returns whether the :attr:`~Variable.outconverter`
method is called when null values are fetched from the database.
.. versionadded:: 1.4.0
.. attribute:: Variable.inconverter
This read-write attribute specifies the method used to convert data from

View File

@ -2,11 +2,10 @@
#
# python-oracledb documentation build configuration file
#
# This file is execfile()d with the current directory set to its containing dir
# This file is execfile()d with the current directory set to its containing dir.
#
# The contents of this file are pickled, so don't put values in the namespace
# that aren't pickleable (module imports are okay, they're removed
# automatically).
# that aren't pickleable (module imports are okay, they're removed automatically).
#
# All configuration values have a default value; values that are commented out
# serve to show the default value.
@ -20,28 +19,23 @@ sys.path.append(os.path.abspath("_ext"))
# General configuration
# ---------------------
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
extensions = ["table_with_summary", "oracle_deprecated", "sphinx_rtd_theme"]
# Add any Sphinx extension module names here, as strings. They can be extensions
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
extensions = ["table_with_summary", "oracle_deprecated"]
# Add any paths that contain templates here, relative to this directory.
templates_path = [".templates"]
templates_path = ['.templates']
# The suffix of source filenames.
source_suffix = ".rst"
source_suffix = '.rst'
# The root toctree document.
root_doc = master_doc = "index"
root_doc = master_doc = 'index'
# General substitutions.
project = "python-oracledb"
copyright = (
"2016, 2023, Oracle and/or its affiliates. All rights reserved. "
"Portions Copyright © 2007-2015, Anthony Tuininga. All rights reserved. "
"Portions Copyright © 2001-2007, Computronix (Canada) Ltd., "
"Edmonton, Alberta, Canada. All rights reserved"
)
author = "Oracle"
project = 'python-oracledb'
copyright = u'2016, 2023, Oracle and/or its affiliates. All rights reserved. Portions Copyright © 2007-2015, Anthony Tuininga. All rights reserved. Portions Copyright © 2001-2007, Computronix (Canada) Ltd., Edmonton, Alberta, Canada. All rights reserved'
author = 'Oracle'
# The default replacements for |version| and |release|, also used in various
# other places throughout the built documents.
@ -62,26 +56,26 @@ release = local_vars["__version__"]
# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
# today = ''
#today = ''
# Else, today_fmt is used as the format for a strftime call.
today_fmt = "%B %d, %Y"
today_fmt = '%B %d, %Y'
# List of documents that shouldn't be included in the build.
# unused_docs = []
#unused_docs = []
# If true, '()' will be appended to :func: etc. cross-reference text.
# add_function_parentheses = True
#add_function_parentheses = True
# If true, the current module name will be prepended to all description
# unit titles (such as .. function::).
# add_module_names = True
#add_module_names = True
# If true, sectionauthor and moduleauthor directives will be shown in the
# output. They are ignored by default.
# show_authors = False
#show_authors = False
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = "sphinx"
pygments_style = 'sphinx'
# Options for HTML output
@ -93,67 +87,64 @@ pygments_style = "sphinx"
# html_style = 'default.css'
# The theme to use for readthedocs.
html_theme = "sphinx_rtd_theme"
html_theme = 'sphinx_rtd_theme'
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = [".static"]
html_static_path = ['.static']
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
# using the given strftime format.
html_last_updated_fmt = "%b %d, %Y"
html_last_updated_fmt = '%b %d, %Y'
# If true, SmartyPants will be used to convert quotes and dashes to
# typographically correct entities.
# html_use_smartypants = True
#html_use_smartypants = True
# Content template for the index page.
# html_index = ''
#html_index = ''
# Custom sidebar templates, maps document names to template names.
# html_sidebars = {}
#html_sidebars = {}
# Additional templates that should be rendered to pages, maps page names to
# template names.
# html_additional_pages = {}
#html_additional_pages = {}
# If false, no module index is generated.
# html_use_modindex = True
#html_use_modindex = True
# If true, the reST sources are included in the HTML build as _sources/<name>.
html_copy_source = False
# Output file base name for HTML help builder.
htmlhelp_basename = "oracledbdoc"
htmlhelp_basename = 'oracledbdoc'
numfig = True
# Display tables with no horizontal scrollbar
def setup(app):
app.add_css_file("custom.css")
app.add_css_file('custom.css')
# Options for LaTeX output
# ------------------------
# The paper size ('letter' or 'a4').
# latex_paper_size = 'letter'
#latex_paper_size = 'letter'
# The font size ('10pt', '11pt' or '12pt').
# latex_font_size = '10pt'
#latex_font_size = '10pt'
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title, author, document class
# [howto/manual]).
# latex_documents = []
# (source start file, target name, title, author, document class [howto/manual]).
#latex_documents = []
# Additional stuff for the LaTeX preamble.
# latex_preamble = ''
#latex_preamble = ''
# Documents to append as an appendix to all manuals.
# latex_appendices = []
#latex_appendices = []
# If false, no module index is generated.
# latex_use_modindex = True
#latex_use_modindex = True

View File

@ -6,11 +6,10 @@ Python-oracledb is the new name for the Python `cx_Oracle driver
an open source module that enables Python programs to access Oracle
Database. It conforms to the `Python Database API v2.0 Specification
<https://www.python.org/dev/peps/pep-0249/>`__ with a considerable number of
additions and a couple of exclusions. Synchronous and
:ref:`concurrent <asyncio>` coding styles are supported.
additions and a couple of exclusions.
This module is currently tested with Python 3.7, 3.8, 3.9, 3.10, 3.11 and 3.12
against Oracle Database 23c, 21c, 19c, 18c, 12c, and 11gR2.
This module is currently tested with Python 3.6, 3.7, 3.8, 3.9, 3.10 and 3.11
against Oracle Database 21c, 19c, 18c, 12c, and 11.2.
**python-oracledb** is distributed under an open-source
:ref:`license <license>`. Changes in python-oracledb releases can be found in
@ -46,11 +45,9 @@ User Guide
user_guide/two_phase_commit.rst
user_guide/startup.rst
user_guide/ha.rst
user_guide/globalization.rst
user_guide/asyncio.rst
user_guide/exception_handling.rst
user_guide/tracing.rst
user_guide/troubleshooting.rst
user_guide/globalization.rst
user_guide/exception_handling.rst
user_guide/appendix_a.rst
user_guide/appendix_b.rst
user_guide/appendix_c.rst
@ -69,17 +66,12 @@ API Manual
api_manual/connection_pool.rst
api_manual/pool_params.rst
api_manual/cursor.rst
api_manual/fetch_info.rst
api_manual/variable.rst
api_manual/subscription.rst
api_manual/lob.rst
api_manual/dbobject_type.rst
api_manual/aq.rst
api_manual/soda.rst
api_manual/async_connection.rst
api_manual/async_connection_pool.rst
api_manual/async_cursor.rst
api_manual/async_lob.rst
api_manual/deprecations.rst
.. toctree::

View File

@ -7,476 +7,6 @@ python-oracledb Release Notes
For deprecations, see :ref:`Deprecations <deprecations>`.
oracledb 2.0.1 (TBD)
--------------------
Thin Mode Changes
+++++++++++++++++
#) Added support for using alternative event loop implementations like uvloop
(`issue 276 <https://github.com/oracle/python-oracledb/issues/276>`__).
#) Added support for the asynchronous context manager protocol on the
AsyncCursor class as a convenience.
#) Fixed regression when connecting to a database using listener redirects
with either a connection pool or using asyncio
(`issue 275 <https://github.com/oracle/python-oracledb/issues/275>`__).
#) Fixed bug with intermittent hang on some versions of Oracle Database when
using asyncio and the database raises an error and output variables are
present
(`issue 278 <https://github.com/oracle/python-oracledb/issues/278>`__).
#) Fixed bug when fetch variables contain output converters and a query is
re-executed
(`issue 271 <https://github.com/oracle/python-oracledb/issues/271>`__).
#) Internal change to ensure that pools are closed gracefully when the main
thread terminates.
#) Internal change to slightly improve performance of LOB reads and writes.
#) Corrected typing declaration for :meth:`oracledb.connect_async()`.
Common Changes
++++++++++++++
#) Fixed regression which prevented a null value from being set on DbObject
attributes or used as elements of collections
(`issue 273 <https://github.com/oracle/python-oracledb/issues/273>`__).
#) Fixed regression from cx_Oracle which ignored the value of the
``encoding_errors`` parameter when creating variables by calling the method
:meth:`Cursor.var()`
(`issue 279 <https://github.com/oracle/python-oracledb/issues/279>`__).
#) Bumped minimum requirement of Cython to 3.0.
oracledb 2.0.0 (December 2023)
------------------------------
Thin Mode Changes
+++++++++++++++++
#) Added support for :ref:`concurrent programming with asyncio <asyncio>`
(`issue 6 <https://github.com/oracle/python-oracledb/issues/6>`__).
#) Added parameter :attr:`ConnectParams.sdu` for configuring the Session Data
Unit (SDU) size for sizing internal buffers used for tuning communication
with the database. The connection property :attr:`Connection.sdu` was also
added.
#) Added parameter :data:`ConnectParams.ssl_context` to modify the SSL context
used when connecting via TLS
(`issue 259 <https://github.com/oracle/python-oracledb/issues/259>`__).
#) Added support for an Oracle Database 23c JSON feature allowing field names
with more than 255 UTF-8 encoded bytes.
#) Added support for the ``FAILOVER`` clause in full connect descriptors.
#) Fixed bug in detecting the current time zone
(`issue 257 <https://github.com/oracle/python-oracledb/issues/257>`__).
#) Fixed bug in handling database response in certain unusual circumstances.
#) Fixed bug in handling exceptions raised during connection establishment.
#) Fixed bug in identifying bind variables in SQL statements containing
multiple line comments with multiple asterisks before the closing slash.
#) A more meaningful error is raised when the wrong type of data is passed to
:meth:`LOB.write()`.
#) Internal change to support an Oracle Database 23c JSON feature improving
JSON storage usage.
#) Internal change to ensure that all connections in a pool have been closed
gracefully before the pool is closed.
#) Internal changes to improve handling of the network protocol between
python-oracledb and Oracle Database.
#) Internal changes to improve handling of multiple address and description
lists in full connect descriptors.
Thick Mode Changes
++++++++++++++++++
#) Fixed bug in return value of :meth:`SodaOperation.replaceOne()`.
Common Changes
++++++++++++++
#) Dropped support for Python 3.6.
#) Desupported a number of parameters and attributes that were previously
deprecated. See :ref:`desupport notices<_desupported_2_0>` for details.
#) Added property :attr:`Cursor.warning` for database warnings (such as PL/SQL
compilation warnings) generated by calls to :meth:`Cursor.execute()` or
:meth:`Cursor.executemany()`.
#) Added property :attr:`Connection.warning` for warnings (such as the password
being in the grace period) generated during connection.
#) Added properties that provide information about the database:
:attr:`Connection.db_domain`, :attr:`Connection.db_name`,
:attr:`Connection.max_open_cursors`, :attr:`Connection.service_name`
and :attr:`Connection.transaction_in_progress`.
#) Added property :data:`Connection.proxy_user` to show the name of the user
which was used as a proxy when connecting (`issue 250
<https://github.com/oracle/python-oracledb/issues/250>`__).
#) Added properties :data:`FetchInfo.domain_schema`,
:data:`FetchInfo.domain_name` and :data:`FetchInfo.annotations` for the
`SQL domain <https://docs.oracle.com/en/database/oracle/oracle-database/
23/sqlrf/create-domain.html#GUID-17D3A9C6-D993-4E94-BF6B-CACA56581F41>`__
and `annotations <https://docs.oracle.com/en/database/oracle/
oracle-database/23/sqlrf/annotations_clause.html#
GUID-1AC16117-BBB6-4435-8794-2B99F8F68052>`__
associated with columns that are being fetched. SQL domains and annotations
require Oracle Database 23c. If using python-oracledb Thick mode, Oracle
Client 23c is also required.
#) Added parameter ``data`` to :meth:`Connection.createlob()` to allow data to
be written at LOB creation time.
#) Added type :data:`~oracledb.DB_TYPE_XMLTYPE` to represent data of type
``SYS.XMLTYPE`` in the database. Previously the value of
:data:`FetchInfo.type_code` for data of this type was
:data:`~oracledb.DB_TYPE_LONG` in Thick mode and
:data:`~oracledb.DB_TYPE_OBJECT` in Thin mode.
#) Attribute and element values of :ref:`Oracle Object <dbobject>` instances
that contain strings or bytes now have their maximum size constraints
checked. Errors ``DPY-2043`` (attributes) and ``DPY-2044`` (element values)
are now raised when constraints are violated.
#) Attribute and element values of :ref:`Oracle Object <dbobject>` instances
that are numbers are now returned as integers if the precision and scale
allow for it. This is the same way that numbers are fetched from the
database
(`issue 99 <https://github.com/oracle/python-oracledb/issues/99>`__).
#) Errors that have entries in the
:ref:`troubleshooting documentation <troubleshooting>` now have links to
that documentation shown in the message text.
#) Fixed bug with binding boolean values with Oracle Database 23c
(`issue 263 <https://github.com/oracle/python-oracledb/issues/263>`__).
#) Fixed bug with getting unknown attributes from :ref:`Oracle Object
<dbobject>` instances.
#) Error ``DPY-4029: errors in array DML exceed 65535`` is now raised when the
number of batch errors exceeds 65535 when calling
:meth:`Cursor.executemany()` with the parameter ``batcherrors`` set to the
value ``True``. Note that in thick mode this error is not raised unless the
number of batch errors is a multiple of 65536; instead, the number of batch
errors returned is modulo 65536
(`issue 262 <https://github.com/oracle/python-oracledb/issues/262>`__).
#) Black is now used to format Python code and ruff to lint Python code.
oracledb 1.4.2 (October 2023)
-----------------------------
Thick Changes
+++++++++++++
#) Fixed bug resulting in a segfault on some platforms when using two-phase
commit.
Common Changes
++++++++++++++
#) Pre-built binaries are now being created for Python 3.12
(`issue 237 <https://github.com/oracle/python-oracledb/issues/237>`__).
oracledb 1.4.1 (September 2023)
-------------------------------
Thin Mode Changes
+++++++++++++++++
#) Improved statement bind variable placeholder parser performance, handle
statements which use the `Alternative Quoting Mechanism
<https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-1824CBAA-6E16-4921-B2A6-112FB02248DA>`__
('Q' strings), and fix some issues identifying bind variable placeholders
in embedded quotes and in JSON syntax.
Thick Changes
+++++++++++++
#) Fixed error checking when getting or setting the connection pool parameters
``ping_interval`` and ``soda_metadata_cache``.
Common Changes
++++++++++++++
#) Fixed bug when calling :meth:`Cursor.execute()` or
:meth:`Cursor.executemany()` with missing bind data after calling
:meth:`Cursor.setinputsizes()` with at least one of the values supplied as
``None``
(`issue 217 <https://github.com/oracle/python-oracledb/issues/217>`__).
#) SQL statement parsing now raises ``DPY-2041: missing ending quote (') in
string`` or ``DPY-2042: missing ending quote (") in identifier`` for
statements with the noted invalid syntax. Previously, thick mode gave
``ORA-1756`` or ``ORA-1740``, respectively, while thin mode did not throw
an error.
#) Added missing ">" to ``repr()`` of :ref:`sodadb`.
oracledb 1.4.0 (August 2023)
----------------------------
Thin Mode Changes
+++++++++++++++++
#) Added support for an Oracle Database 23c feature that can improve the
performance of connection creation by reducing the number of round trips
required to create the second and subsequent connections to the same
database.
#) Added support for shrinking the connection pool back to the specified
minimum size when the pool is idle for :data:`ConnectionPool.timeout`
seconds.
#) Added support for growing the connection pool back to the minimum number of
connections after connections are killed or otherwise made unusable.
#) A default connection class is now generated when DRCP is used with a
connection pool and no connection class was specified when the pool was
created. The default connection class will be of the form ``DPY:`` followed
by a 16-byte unique identifier converted to base64 encoding.
#) Changed internal connection feature negotiation for more accurate Oracle
Database 23c support.
#) Added support for sending a generated connection identifier to the
database used for tracing. An application specific prefix is prepended to
this value if specified via a new ``connection_id_prefix`` parameter when
creating standalone connections or connection pools.
#) Added URL to the Oracle Database Error Help Portal in Oracle Database
error messages similar to when Thick mode uses Oracle Client 23c.
#) Added support for the ``ORA_SDTZ`` environment variable used to set the
session time zone used by the database.
#) Fixed bug when a dynamically sized connection pool is created with an
``increment`` of zero and the pool needs to grow.
#) Fixed bug affecting connection reuse when connections were acquired from
the connection pool with a ``cclass`` different to the one used to
create the pool.
#) Fixed bug when a connection is discarded from the connection pool during
:meth:`ConnectionPool.acquire()` and the ping check fails due to the
connection being dead.
#) Fixed bug when an output type handler is used and the value of
:attr:`Cursor.prefetchrows` exceeds :attr:`Cursor.arraysize`
(`issue 173 <https://github.com/oracle/python-oracledb/issues/173>`__).
#) Fixed bug when an Application Continuity replay context is returned during
connection to the database
(`issue 176 <https://github.com/oracle/python-oracledb/issues/176>`__).
#) Fixed bug when socket is not closed immediately upon failure to establish a
connection to the database
(`issue 211 <https://github.com/oracle/python-oracledb/issues/211>`__).
Thick Mode Changes
++++++++++++++++++
#) Added function :meth:`SodaCollection.listIndexes()` for getting the indexes
on a SODA collection.
#) Added support for specifying if documents should be locked when fetched
from SODA collections. A new non-terminal method
:meth:`~SodaOperation.lock()` was added which requires Oracle Client
21.3 or higher (or Oracle Client 19 from 19.11).
#) Relaxed restriction for end-to-end tracing string connection
attributes. These values can now be set to the value ``None`` which will be
treated the same as an empty string.
#) Fixed bug when using external authentication with an Easy Connect
connection string.
#) Fixed memory leak when accessing objects embedded within other objects.
Common Changes
++++++++++++++
#) Use of Python 3.6 and 3.7 is deprecated and support for them will be
removed in a future release. A warning is issued when these versions are
used but otherwise they will continue to function as usual. The warning can
be suppressed by importing `warnings
<https://docs.python.org/3/library/warnings.html>`__ and adding a call like
``warnings.filterwarnings(action='ignore', module="oracledb")``
*before* importing ``oracledb``.
#) Added support for the :attr:`~Variable.outconverter` being called when a
null value is fetched from the database and the new parameter
``convert_nulls`` to the method :meth:`Cursor.var()` is passed the value
``True``
(`issue 107 <https://github.com/oracle/python-oracledb/issues/107>`__).
#) Replaced fixed 7-tuple for the cursor metadata found in
:data:`Cursor.description` with a class which provides additional
information such as the database object type and whether the column
contains JSON data.
#) Changed the signature for output type handlers to
``handler(cursor, metadata)`` where the ``metadata`` parameter is a
:ref:`FetchInfo<fetchinfoobj>` object containing the same information found
in :data:`Cursor.description`. The original signature for output type
handlers is deprecated and will be removed in a future version.
#) Added support for fetching VARCHAR2 and LOB columns which contain JSON (and
have the "IS JSON" check constraint enabled) in the same way as columns of
type JSON (which requires Oracle Database 21c or higher) are fetched. In
thick mode this requires Oracle Client 19c or higher. The attribute
``oracledb.__future__.old_json_col_as_obj`` must be set to the value
``True`` for this behavior to occur. In version 2.0 this will become the
normal behavior and setting this attribute will no longer be needed.
#) Added new property :attr:`Connection.instance_name` which provides the
Oracle Database instance name associated with the connection. This is the
same value as the SQL expression
``sys_context('userenv', 'instance_name')``.
#) Added support for relational queries on the underlying tables of SODA
collections created in Oracle Database 23c if they contain JSON documents
with embedded OIDs.
#) Automatically retry a query if the error ``ORA-00932: inconsistent data
types`` is raised (which can occur if a table or view is recreated with a
data type that is incompatible with the column's previous data type).
#) The ``repr()`` value of the DbObject class now shows the string "DbObject"
instead of the string "Object" for consistency with the name of the class
and the other ``repr()`` values for DbObjectType and DbObjectAttr.
#) Fixed bug when binding sequences other than lists and tuples
(`issue 205 <https://github.com/oracle/python-oracledb/issues/205>`__).
#) Added support for using the Cython 3.0 release
(`issue 204 <https://github.com/oracle/python-oracledb/issues/204>`__).
#) Improved test suite and documentation.
oracledb 1.3.2 (June 2023)
--------------------------
Thin Mode Changes
+++++++++++++++++
#) Fixed bug using :attr:`Cursor.arraysize` for tuning data fetches from REF
CURSORS.
#) Fixed bug connecting to databases with older 11g password verifiers
(`issue 189 <https://github.com/oracle/python-oracledb/issues/189>`__).
#) Fixed bugs in the implementation of the statement cache.
#) Fixed bug which caused a cursor leak if an error was thrown while
processing the execution of a query.
#) Eliminated unneeded round trip when using token authentication to connect
to the database.
#) Fixed bug which could cause a redirect loop with improperly configured
listener redirects.
#) Fixed bug when executing PL/SQL with a large number of binds.
#) Fixed bug when using DRCP with Oracle Database 23c.
Thick Mode Changes
++++++++++++++++++
#) Fixed bug when using external authentication with a Net Service Name
connection string
(`issue 178 <https://github.com/oracle/python-oracledb/issues/178>`__).
#) Fixed bug when using external authentication with an Easy Connect
connection string.
Common Changes
++++++++++++++
#) When fetching rows from REF CURSORS, the cursor's
:attr:`~Cursor.prefetchrows` attribute is now ignored. Use
:attr:`Cursor.arraysize` for tuning these fetches. This change allows
consistency between Thin and Thick modes.
oracledb 1.3.1 (April 2023)
---------------------------
Thin Mode Changes
+++++++++++++++++
#) Improved performance of regular expressions used for parsing SQL
(`issue 172 <https://github.com/oracle/python-oracledb/issues/172>`__).
#) Fixed bug with Oracle Database 23c when SQL is executed after first being
parsed.
#) Fixed bug when :data:`ConnectionPool.timeout` is not `None` when creating a
connection pool
(`issue 166 <https://github.com/oracle/python-oracledb/issues/166>`__).
#) Fixed bug when a query is re-executed after an underlying table is dropped
and recreated, and the query select list contains LOBs or JSON data.
#) Fixed bug when warning message such as for impending password expiry is
encountered during connect
(`issue 171 <https://github.com/oracle/python-oracledb/issues/171>`__).
Common Changes
++++++++++++++
#) Improved test suite and samples.
oracledb 1.3.0 (March 2023)
---------------------------
Thin Mode Changes
+++++++++++++++++
#) Added direct support for the Oracle Database 21c JSON data type, removing
the need to use an output type handler.
#) Added implementation for :data:`ConnectionPool.timeout` to allow pools to
shrink to ``min`` connections.
#) Added check to prevent adding too many elements to bounded database
collections.
#) Removed internally set fixed size for database collections. Collections of
any size supported by the database can now be created.
#) Added support for connecting to databases that accept passwords longer than
30 UTF-8 encoded bytes.
#) Detect the time zone on the OS and set the session timezone using this
value to be consistent with thick mode
(`issue 144 <https://github.com/oracle/python-oracledb/issues/144>`__).
#) Improved BOOLEAN handling.
#) Error ``DPY-6005: cannot connect to database`` is now raised for all
failures to connect to the database and the phrase ``cannot connect to
database`` is removed from all other error messages (since this can be
confusing when these errors are raised from
:meth:`ConnectParams.parse_connect_string()`).
#) Fixed bug when calling :meth:`Cursor.executemany()` with PL/SQL when the
size of the bound data increases on subsequent calls
(`issue 132 <https://github.com/oracle/python-oracledb/issues/132>`__).
#) Fixed bug when binding data of type TIMESTAMP WITH TIME ZONE but with
zero fractional seconds.
#) Fixed bug with incorrect values of :data:`Cursor.rowcount` when fetching
data
(`issue 147 <https://github.com/oracle/python-oracledb/issues/147>`__).
#) Fixed bug with SQL containing multibyte characters with certain database
character sets
(`issue 133 <https://github.com/oracle/python-oracledb/issues/133>`__).
#) Fixed bug with ordering of binds in SQL when the database version is 12.1
(`issue 135 <https://github.com/oracle/python-oracledb/issues/135>`__).
#) Fixed bug with ordering of binds in PL/SQL when the bind variable may
potentially exceed the 32767 byte limit but the actual value bound does not
(`issue 146 <https://github.com/oracle/python-oracledb/issues/146>`__).
#) Fixed bug connecting to an IPv6 address with IAM tokens.
#) Fixed bug determining RETURNING binds in a SQL statement when RETURNING and
INTO keywords are not separated by whitespace, but are separated by
parentheses.
#) The exception ``DPY-3022: named time zones are not supported in thin mode``
is now raised when attempting to fetch data of type TIMESTAMP WITH TIME
ZONE when the time zone associated with the data is a named time zone.
Previously invalid data was returned
(`disc 131 <https://github.com/oracle/python-oracledb/discussions/131>`__).
#) Internal implementation changes:
- Added internal support for prefetching the LOB size and chunk size,
thereby eliminating a :ref:`round-trip<roundtrips>` when calling
:meth:`LOB.size()` and :meth:`LOB.getchunksize()`.
- Made the pool implementation LIFO to improve locality, reduce the number
of times any session callback must be invoked, and allow connections to
be timed out.
- Removed packet for negotiating network services which are not supported
in thin mode.
- Removed unneeded packet for changing the password of the connected user.
Thick Mode Changes
++++++++++++++++++
#) Raise a more meaningful error when an unsupported type in a JSON value is
detected.
#) Added support for the "signed int", "signed long" and "decimal128" scalar
types in JSON (generally only seen when converting from MongoDB).
#) Defer raising an exception when calling :meth:`Connection.gettype()`
for a type containing an attribute or element with an unsupported data type
until the first attempt to reference the attribute or element with the
unsupported data type.
#) Fixed bug when attempting to create bequeath connections when the DSN
contains credentials.
Common Changes
++++++++++++++
#) Improved type annotations.
#) Added method :meth:`ConnectParams.parse_dsn_with_credentials()` for parsing
a DSN that contains credentials.
#) Error ``DPY-2038: element at index {index} does not exist`` is now raised
whenever an element in a database collection is missing. Previously, thick
mode raised ``DPI-1024: element at index {index} does not exist`` and thin
mode raised ``KeyError`` or ``IndexError``.
#) Error ``DPY-2039: given index {index} must be in the range of {min_index}
to {max_index}`` is now raised whenever an element in a database collection
is set outside the bounds of the collection. Previously, thick mode raised
``OCI-22165: given index [{index}] must be in the range of [{min_index}] to
[{max_index}]`` and thin mode raised ``IndexError``.
#) Error ``DPY-2040: parameters "batcherrors" and "arraydmlrowcounts" may only
be true when used with insert, update, delete and merge statements`` is now
raised when either of the parameters `batcherrors` and `arraydmlrowcounts`
is set to the value `True` when calling :meth:`Cursor.executemany()`.
Previously, thick mode raised ``DPI-1063: modes DPI_MODE_EXEC_BATCH_ERRORS
and DPI_MODE_EXEC_ARRAY_DML_ROWCOUNTS can only be used with insert, update,
delete and merge statements`` and thin mode raised
``ORA-03137: malformed TTC packet from client rejected``
(`issue 128 <https://github.com/oracle/python-oracledb/issues/128>`__).
#) Internal changes to ensure that errors taking place while raising
exceptions are handled more gracefully.
oracledb 1.2.2 (January 2023)
-----------------------------

View File

@ -86,22 +86,14 @@ see :ref:`driverdiff` and :ref:`compatibility`.
- No
- Yes
- Yes
* - Lightweight Directory Access Protocol (LDAP) connections
- No
- Yes
- Yes
* - Proxy connections (see :ref:`proxyauth`)
- Yes
- Yes
- Yes
* - Socket Secure (SOCKS) Proxy connections
- No
- No
- No
* - Connection mode privileges (see :ref:`connection-authorization-modes`)
- Yes
- Yes - only :data:`~oracledb.AUTH_MODE_SYSDBA` is supported
- Yes - only :data:`~oracledb.AUTH_MODE_SYSDBA` is supported
- Yes - only :data:`~oracledb.AUTH_MODE_SYSDBA` is supported in Thick mode
- Yes
* - Preliminary connections
- No
- Yes
@ -122,7 +114,7 @@ see :ref:`driverdiff` and :ref:`compatibility`.
- No
- Yes - No TIMESTAMP support
- Yes - No TIMESTAMP support
* - Oracle Database Native Network Encryption (NNE) (see :ref:`nne`)
* - Oracle Database Native Network Encryption (see :ref:`nne`)
- No
- Yes
- Yes
@ -186,7 +178,7 @@ see :ref:`driverdiff` and :ref:`compatibility`.
- Yes for scalar types. Yes for collection types using array interface.
- Yes
- Yes
* - Simple Oracle Document Access (SODA) API (see :ref:`SODA <soda>`)
* - Simple Oracle Document Access (SODA) API (:ref:`SODA <soda>`)
- No
- Yes
- Yes
@ -214,10 +206,10 @@ see :ref:`driverdiff` and :ref:`compatibility`.
- No - All NLS environment variables are ignored. Use Python globalization support instead
- Yes - NLS environment variables are respected except character set in NLS_LANG
- Yes - NLS environment variables are respected except character set in NLS_LANG
* - Row prefetching on first query execute (see :attr:`prefetchrows`)
- Yes - unless the row contains LOBs or similar types
- Yes - unless the row contains LOBs or similar types
- Yes - unless the row contains LOBs or similar types
* - Row prefetching on first query execute.(see :attr:`prefetchrows`)
- Yes
- Yes
- Yes
* - Array fetching for queries (see :attr:`arraysize`)
- Yes
- Yes
@ -282,10 +274,6 @@ see :ref:`driverdiff` and :ref:`compatibility`.
- No
- Yes
- Yes
* - Concurrent programming with asyncio (see :ref:`asyncio`)
- Yes
- No
- No
* - End-to-end monitoring and tracing attributes (see :ref:`tracingsql`)
- Yes
- Yes
@ -300,7 +288,7 @@ see :ref:`driverdiff` and :ref:`compatibility`.
- Yes
* - Two-phase Commit (TPC)
- No
- Yes - improved support. See :ref:`tcp`.
- Yes - improved support (see :ref:`tcp`)
- Yes - limited support
* - REF CURSORs and Nested Cursors
- Yes
@ -322,14 +310,70 @@ see :ref:`driverdiff` and :ref:`compatibility`.
- Yes
- Yes
- Yes
* - LOB length prefetching
- Yes
- Yes
- Yes
* - LOB prefetching
- No
- No - does have LOB length prefetch
- No - does have LOB length prefetch
* - LOB locator operations such as trim
- Yes
- Yes
- Yes
* - CHAR, VARCHAR2, NUMBER, FLOAT, DATE, and LONG data types
- Yes
- Yes
- Yes
* - BLOB and CLOB data types
- Yes
- Yes
- Yes
* - BINARY_DOUBLE and BINARY_FLOAT data types
- Yes
- Yes
- Yes
* - RAW and LONG RAW data types
- Yes
- Yes
- Yes
* - INTERVAL DAY TO SECOND data type (see :data:`~oracledb.DB_TYPE_INTERVAL_DS`)
- Yes
- Yes
- Yes
* - INTERVAL YEAR TO MONTH data type (see :data:`~oracledb.DB_TYPE_INTERVAL_YM`)
- No
- No
- No
* - Oracle 12c JSON
- Yes
- Yes
- Yes
* - Oracle 21c JSON data type (see :data:`~oracledb.DB_TYPE_JSON`)
- No - can fetch with an output type handler, see :ref:`Fetching JSON Differences <fetchJSON>`
- Yes
- Yes
* - ROWID, UROWID data types
- Yes
- Yes
- Yes
* - TIMESTAMP, TIMESTAMP WITH TIME ZONE, TIMESTAMP WITH LOCAL TIME ZONE data types
- Yes
- Yes
- Yes
* - NCHAR, NVARCHAR2, NCLOB data types
- Yes
- Yes
- Yes
* - PL/SQL data types BOOLEAN, PLS_INTEGER and BINARY_INTEGER
- Yes
- Yes
- Yes
* - XMLType data type (see :ref:`xmldatatype`)
- Yes
- Yes - may need to fetch as CLOB
- Yes - may need to fetch as CLOB
* - BFILE data type (see :data:`~oracledb.DB_TYPE_BFILE`)
- No
- Yes
- Yes
.. _supporteddbtypes:
@ -350,133 +394,198 @@ values.
:header-rows: 1
:class: wy-table-responsive
:align: center
:summary: The first column displays the database data type. The second column displays the python-oracledb constant Name. The third column contains notes. The fourth column shows Python types that can be used.
:summary: The first column displays the database data type. The second column displays the python-oracledb constant Name. The third column indicates if the type is supported in python-oracledb.
* - Oracle Database Type
- python-oracledb Constant Name
- Notes
- Supported in python-oracledb
- Supported Python Types
* - VARCHAR2
- :data:`~oracledb.DB_TYPE_VARCHAR`
-
- DB_TYPE_VARCHAR
- Yes
- bytes, str
* - NVARCHAR2
- :data:`~oracledb.DB_TYPE_NVARCHAR`
-
- DB_TYPE_NVARCHAR
- Yes
- bytes, str
* - NUMBER, FLOAT
- :data:`~oracledb.DB_TYPE_NUMBER`
-
- DB_TYPE_NUMBER
- Yes
- bool, int, float, decimal.Decimal
* - DATE
- :data:`~oracledb.DB_TYPE_DATE`
-
- DB_TYPE_DATE
- Yes
- datetime.date, datetime.datetime
* - BOOLEAN (PL/SQL and Oracle Database 23c SQL)
- :data:`~oracledb.DB_TYPE_BOOLEAN`
-
- Any type convertible to bool
* - BOOLEAN (PL/SQL)
- DB_TYPE_BOOLEAN
- Yes
- ANY (converted to bool)
* - BINARY_DOUBLE
- :data:`~oracledb.DB_TYPE_BINARY_DOUBLE`
-
- DB_TYPE_BINARY_DOUBLE
- Yes
- bool, int, float, decimal.Decimal
* - BINARY_FLOAT
- :data:`~oracledb.DB_TYPE_BINARY_FLOAT`
-
- DB_TYPE_BINARY_FLOAT
- Yes
- bool, int, float, decimal.Decimal
* - TIMESTAMP
- :data:`~oracledb.DB_TYPE_TIMESTAMP`
-
- DB_TYPE_TIMESTAMP
- Yes
- datetime.date, datetime.datetime
* - TIMESTAMP WITH TIME ZONE
- :data:`~oracledb.DB_TYPE_TIMESTAMP_TZ`
-
- DB_TYPE_TIMESTAMP_TZ
- Yes
- datetime.date, datetime.datetime
* - TIMESTAMP WITH LOCAL TIME ZONE
- :data:`~oracledb.DB_TYPE_TIMESTAMP_LTZ`
-
- DB_TYPE_TIMESTAMP_LTZ
- Yes
- datetime.date, datetime.datetime
* - INTERVAL YEAR TO MONTH
- :data:`~oracledb.DB_TYPE_INTERVAL_YM`
- Not supported in python-oracledb.
- Cannot be set
- DB_TYPE_INTERVAL_YM
- Not supported in python-oracledb
- cannot be set
* - INTERVAL DAY TO SECOND
- :data:`~oracledb.DB_TYPE_INTERVAL_DS`
-
- DB_TYPE_INTERVAL_DS
- Yes
- datetime.timedelta
* - RAW
- :data:`~oracledb.DB_TYPE_RAW`
-
- DB_TYPE_RAW
- Yes
- bytes, str
* - LONG
- :data:`~oracledb.DB_TYPE_LONG`
-
- DB_TYPE_LONG
- Yes
- bytes, str
* - LONG RAW
- :data:`~oracledb.DB_TYPE_LONG_RAW`
-
- DB_TYPE_LONG_RAW
- Yes
- bytes, str
* - ROWID
- :data:`~oracledb.DB_TYPE_ROWID`
-
- DB_TYPE_ROWID
- Yes
- bytes, str
* - UROWID
- :data:`~oracledb.DB_TYPE_ROWID`, :data:`~oracledb.DB_TYPE_UROWID` (only supported in python-oracledb Thin mode)
- May show :data:`~oracledb.DB_TYPE_UROWID` in metadata. See :ref:`Query Metadata Differences <querymetadatadiff>`.
- DB_TYPE_ROWID, DB_TYPE_UROWID (only supported in python-oracledb Thin mode)
- Yes. May show DB_TYPE_UROWID in metadata. See :ref:`Query Metadata Differences <querymetadatadiff>`.
- bytes, str
* - CHAR
- :data:`~oracledb.DB_TYPE_CHAR`
-
- DB_TYPE_CHAR
- Yes
- bytes, str
* - BLOB
- :data:`~oracledb.DB_TYPE_BLOB`
-
- :ref:`oracledb.LOB <lobobj>`, bytes, str
- DB_TYPE_BLOB
- Yes
- BLOB, bytes, str
* - CLOB
- :data:`~oracledb.DB_TYPE_CLOB`
-
- :ref:`oracledb.LOB <lobobj>`, bytes, str
- DB_TYPE_CLOB
- Yes
- CLOB, bytes, str
* - NCHAR
- :data:`~oracledb.DB_TYPE_NCHAR`
-
- DB_TYPE_NCHAR
- Yes
- bytes, str
* - NCLOB
- :data:`~oracledb.DB_TYPE_NCLOB`, :data:`~oracledb.DB_TYPE_LONG_NVARCHAR` (if fetching NCLOB as a string)
-
- :ref:`oracledb.LOB <lobobj>`, bytes, str
- DB_TYPE_NCLOB
- Yes
- NCLOB, bytes, str
* - BFILE
- :data:`~oracledb.DB_TYPE_BFILE`
- Not supported in python-oracledb Thin mode.
- Cannot be set
- DB_TYPE_BFILE
- Not supported in python-oracledb Thin mode
- cannot be set
* - JSON
- :data:`~oracledb.DB_TYPE_JSON`
-
- Any type convertible to Oracle JSON
- DB_TYPE_JSON
- Yes. In python-oracledb Thin mode use an output type handler to fetch this Oracle Database 21c data type. See :ref:`jsondatatype`.
- ANY (converted)
* - REF CURSOR (PL/SQL OR nested cursor)
- :data:`~oracledb.DB_TYPE_CURSOR`
-
- :ref:`oracledb.Cursor <cursorobj>`
- DB_TYPE_CURSOR
- Yes
- CURSOR
* - PLS_INTEGER
- :data:`~oracledb.DB_TYPE_BINARY_INTEGER`
-
- DB_TYPE_BINARY_INTEGER
- Yes
- bool, int, float, decimal.Decimal
* - BINARY_INTEGER
- :data:`~oracledb.DB_TYPE_BINARY_INTEGER`
-
- DB_TYPE_BINARY_INTEGER
- Yes
- bool, int, float, decimal.Decimal
* - REF
- n/a
- Not supported in python-oracledb Thin mode
- n/a
* - XMLType
- :data:`~oracledb.DB_TYPE_XMLTYPE`
- May need to use ``xmltype.getclobval()`` to fetch in python-oracledb Thick mode. See :ref:`xmldatatype`
- bytes, str
- n/a
- Not supported in python-oracledb. Use ``xmltype.getclobval()`` to fetch.
- n/a
* - User-defined types (object type, VARRAY, records, collections, SDO_*types)
- :data:`~oracledb.DB_TYPE_OBJECT`
-
- DB_TYPE_OBJECT
- Yes
- OBJECT of specific type
Binding of contiguous PL/SQL Index-by BINARY_INTEGER arrays of string, number, and date are
supported in python-oracledb Thin and Thick modes. Use :meth:`Cursor.arrayvar()` to build
these arrays.
.. Python Types supported for each Oracle Database Type are shown below... list-table-with-summary:: Oracle Database Types Supported
:header-rows: 1
:align: center
:summary: The first column displays the Oracle Database type. The second column displays the Python types that are supported for each of the database types.
* - Oracle Database Type
- Python Types supported
* - DB_TYPE_BFILE
- cannot be set
* - DB_TYPE_BINARY_DOUBLE
- bool, int, float, decimal.Decimal
* - DB_TYPE_BINARY_FLOAT
- bool, int, float, decimal.Decimal
* - DB_TYPE_BINARY_INTEGER
- bool, int, float, decimal.Decimal
* - DB_TYPE_BLOB
- BLOB, bytes, str
* - DB_TYPE_BOOLEAN
- ANY (converted to bool)
* - DB_TYPE_CHAR
- bytes, str
* - DB_TYPE_CLOB
- CLOB, bytes, str
* - DB_TYPE_CURSOR
- CURSOR
* - DB_TYPE_DATE
- datetime.date, datetime.datetime
* - DB_TYPE_INTERVAL_DS
- datetime.timedelta
* - DB_TYPE_INTERVAL_YM
- cannot be set
* - DB_TYPE_JSON
- ANY (converted)
* - DB_TYPE_LONG
- bytes, str
* - DB_TYPE_LONG_NVARCHAR
- bytes, str
* - DB_TYPE_LONG_RAW
- bytes, str
* - DB_TYPE_NCHAR
- bytes, str
* - DB_TYPE_NCLOB
- NCLOB, bytes, str
* - DB_TYPE_NUMBER
- bool, int, float, decimal.Decimal
* - DB_TYPE_NVARCHAR
- bytes, str
* - DB_TYPE_OBJECT
- OBJECT of specific type
* - DB_TYPE_RAW
- bytes, str
* - DB_TYPE_ROWID
- bytes, str
* - DB_TYPE_TIMESTAMP
- datetime.date, datetime.datetime
* - DB_TYPE_TIMESTAMP_LTZ
- datetime.date, datetime.datetime
* - DB_TYPE_TIMESTAMP_TZ
- datetime.date, datetime.datetime
* - DB_TYPE_UROWID
- bytes, str
* - DB_TYPE_VARCHAR
- bytes, str

View File

@ -152,7 +152,7 @@ In python-oracledb Thin mode, using the ``POOL_CONNECTION_CLASS`` or
equivalent attributes when creating a connection or connection pool.
In python-oracledb Thick mode, the ``POOL_CONNECTION_CLASS`` or ``POOL_PURITY``
values will only work when connected to Oracle Database 21c, or later. Note if
values will only work when connected to Oracle Database 21c. Note if
``POOL_PURITY=SELF`` is used in a connect string, then python-oracledb Thick
mode applications will ignore the action to drop the session when attempting to
remove an unusable connections from a pool in some uncommon error cases. It is
@ -164,6 +164,11 @@ attributes.
The ``ENABLE=BROKEN`` connect descriptor option is not supported in
python-oracledb Thin mode. Use ``expire_time`` instead.
The ``Session Data Unit (SDU)`` connect descriptor option that is used to tune
network transfers is not supported in python-oracledb Thin mode. The value is
hard-coded as 8 KB. In python-oracledb Thick mode, the SDU connect descriptor
option and equivalent ``sqlnet.ora`` setting are used.
If a name is given as a connect string, then the python-oracledb Thin mode will
consider it as a Net Service Name and not as the minimal Easy Connect string of
a hostname. The given connect string will be looked up in a ``tnsnames.ora``
@ -212,11 +217,7 @@ See :ref:`enablingthick`.
For example, if you use python-oracledb Thin mode and try to connect to the
Oracle Cloud Infrastructure (OCI) Oracle Base Database where by default native
network encryption is set to REQUIRED in the ``sqlnet.ora`` file of the OCI
Oracle Base Database server, the connection will fail with an error like::
DPY-4011: the database or network closed the connection
or::
Oracle Base Database server, the connection will fail with the error::
DPY-6000: cannot connect to database. Listener refused connection.
(Similar to ORA-12660)
@ -316,6 +317,15 @@ not supported in the Oracle Client libraries that are used in python-oracledb
Thick mode. Note changing the type of bind variables for the same SQL text is
inappropriate and gives indeterminate results in both modes.
.. _fetchJSON:
Fetching JSON in Thin and Thick Modes
=====================================
The python-oracledb Thin mode does not natively handle the Oracle Database 21c
JSON data type but a type handler can be used when fetching the type, see
:ref:`jsondatatype`.
Error Handling in Thin and Thick Modes
======================================
@ -325,8 +335,9 @@ The python-oracledb Thin and Thick modes handle some errors differently. See
Globalization in Thin and Thick Modes
=====================================
All NLS environment variables, and the ``ORA_TZFILE`` environment variable, are
ignored by the python-oracledb Thin mode. Use Python's capabilities instead.
All NLS environment variables, and the ``ORA_SDTZ`` and ``ORA_TZFILE``
environment variables, are ignored by the python-oracledb Thin mode. Use
Python's capabilities instead.
The python-oracledb Thin mode can only use NCHAR, NVARCHAR2, and NCLOB data
when Oracle Database's secondary character set is AL16UTF16.

View File

@ -89,7 +89,7 @@ from cx_Oracle:
can be used to encapsulate connection properties. See :ref:`usingconnparams`
for more information.
- The following parameters are desupported:
- The following parameters are deprecated and ignored:
- ``encoding`` and ``nencoding``: The encodings in use are always UTF-8.
@ -348,9 +348,8 @@ to python-oracledb:
See :ref:`Features Supported <featuresummary>` for details.
- python-oracledb can be used in SQLAlchemy, Django, Pandas, and other
frameworks and Object-relational Mappers (ORMs). To use python-oracledb in
versions of these libraries that don't have native support for the new name,
you can override the use of cx_Oracle with a few lines of code. See
frameworks and Object-relational Mappers (ORMs). Until they add native
support, you can override the use of cx_Oracle with a few lines of code. See
:ref:`frameworks`.
- python-oracledb connection and pool creation calls require keyword arguments
@ -368,8 +367,8 @@ to python-oracledb:
oracledb.connect("scott", pw, "localhost/orclpdb")
- The python-oracledb Thin mode ignores all NLS environment variables. It also
ignores the ``ORA_TZFILE`` environment variable. Thick mode does use these
variables. See :ref:`globalization` for alternatives.
ignores ``ORA_SDTZ`` and ``ORA_TZFILE`` environment variables. Thick mode does use
these variables. See :ref:`globalization` for alternatives.
- To use a ``tnsnames.ora`` file in the python-oracledb Thin mode, you must
explicitly set the environment variable ``TNS_ADMIN`` to the directory
@ -485,15 +484,15 @@ following steps:
6. The default value of the ``oracledb.SessionPool()`` parameter
:attr:`~Connection.getmode` now waits for an available connection. That is the
default is now :data:`~oracledb.POOL_GETMODE_WAIT` instead of
:data:`~oracledb.POOL_GETMODE_NOWAIT`. The new default value improves the
default is now :data:`~oracledb.SPOOL_ATTRVAL_WAIT` instead of
:data:`~oracledb.SPOOL_ATTRVAL_NOWAIT`. The new default value improves the
behavior for most applications. If the pool is in the middle of growing, the
new value prevents transient connection creation errors from occurring when
using the Thin mode, or when using the Thick mode with recent Oracle
Client libraries.
If the old default value is required, modify any pool creation code to
explicitly specify ``getmode=oracledb.POOL_GETMODE_NOWAIT``.
explicitly specify ``getmode=oracledb.POOL_SPOOL_ATTRVAL_NOWAIT``.
Note a :ref:`ConnectionPool class <connpool>` deprecates the equivalent
SessionPool class. The method :meth:`oracledb.create_pool()` deprecates the
@ -517,7 +516,7 @@ following steps:
the code.
9. Modernize code as needed or desired. See :ref:`deprecations` for the list
of deprecations in python-oracledb.
of deprecations in python-oracledb 1.0.
Additional Upgrade Steps to use python-oracledb Thin Mode
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++
@ -559,7 +558,8 @@ addition to the common :ref:`commonupgrade`:
this behavior is also similar in recent versions of the Oracle Call
Interface (OCI) Session Pool used by the Thick mode. Unless the
``oracledb.SessionPool()`` function's parameter ``getmode`` is
:data:`oracledb.POOL_GETMODE_WAIT`, then applications should not call
``SPOOL_ATTRVAL_WAIT`` (or the new equivalent
:data:`oracledb.POOL_GETMODE_WAIT`), then applications should not call
:meth:`ConnectionPool.acquire()` until sufficient time has passed for
connections in the pool to be created.
@ -676,9 +676,9 @@ Python Frameworks, SQL Generators, and ORMs
The python-oracledb Thin mode features in the python-oracledb cover the needs
of frameworks that depend upon the Python Database API.
For versions of SQLAlchemy, Django, other frameworks, object-relational mappers
(ORMs), and libraries that don't have native support for python-oracledb, you
can add temporary code like this to use python-oracledb in-place of cx_Oracle:
Until SQLAlchemy, Django, other frameworks, object-relational mappers (ORMs),
and libraries add native support for python-oracledb, you can add temporary
code like this to use python-oracledb in-place of cx_Oracle:
.. code-block:: python
@ -692,7 +692,3 @@ can add temporary code like this to use python-oracledb in-place of cx_Oracle:
The import of cx_Oracle occurs last. This code must be run before the
library code does its own import of cx_Oracle.
SQLAlchemy 2 and Django 5 have native support for python-oracledb so this code
snippet is not needed in those versions.

View File

@ -1,292 +0,0 @@
.. _asyncio:
***********************************
Concurrent Programming with asyncio
***********************************
The `Asynchronous I/O (asyncio) <https://docs.python.org/3/library/asyncio.
html>`__ Python library can be used with python-oracledb Thin mode for
concurrent programming. This library allows you to run operations in parallel,
for example to run a long-running operation in the background without blocking
the rest of the application. With asyncio, you can easily write concurrent code
with the ``async`` and ``await`` syntax. See Python's `Developing with asyncio
<https://docs.python.org/3/library/asyncio-dev.html>`__ documentation for
useful tips.
.. note::
The asyncio support in python-oracledb 2.0.0 is a pre-release and may
change in the next version.
The python-oracledb asynchronous API is a part of the standard python-oracledb
module. All the synchronous methods that require a round-trip to the database
now have corresponding asynchronous counterparts. You can choose whether to
use the synchronous API or the asynchronous API in your code. It is
recommended to *not* use both at the same time in your application.
The asynchronous API classes are :ref:`AsyncConnection <asyncconnobj>`,
:ref:`AsyncConnectionPool <asyncconnpool>`,
:ref:`AsyncCursor <asynccursorobj>`, and :ref:`AsyncLOB <asynclobobj>`.
.. note::
Concurrent programming with asyncio is only supported in the
python-oracledb Thin mode.
.. _connasync:
Connecting to Oracle Database Asynchronously
============================================
With python-oracledb, you can create an asynchronous connection to Oracle
Database using either :ref:`standalone connections <asyncstandalone>` or
:ref:`pooled connections <asyncconnpool>`. (For discussion of synchronous
programming, see :ref:`connhandling`.)
.. _asyncstandalone:
Standalone Connections
----------------------
Standalone connections are useful for applications that need only a single
connection to a database.
An asynchronous standalone connection can be created by calling the
asynchronous method :meth:`oracledb.connect_async()` which establishes a
connection to the database and returns an :ref:`AsyncConnection Object
<asyncconnobj>`. Once connections are created, all objects created by these
connections follow the asynchronous programming model. Subject to appropriate
use of ``await`` for calls that require a round-trip to the database,
asynchronous connections are used in the same way that synchronous programs use
:ref:`standaloneconnection`.
Asynchronous connections should be released when they are no longer needed to
ensure Oracle Database gracefully cleans up. A preferred method is to use an
asynchronous context manager. For example:
.. code-block:: python
import asyncio
import oracledb
async def main():
async with oracledb.connect_async(user="hr", password=userpwd,
dsn="localhost/orclpdb") as connection:
with connection.cursor() as cursor:
await cursor.execute("select user from dual")
async for result in cursor:
print(result)
asyncio.run(main())
This code ensures that once the block is completed, the connection is closed
and resources are reclaimed by the database. In addition, any attempt to use
the variable ``connection`` outside of the block will fail.
If you do not use a context manager, you should explicitly close connections
when they are no longer needed, for example:
.. code-block:: python
connection = await oracle.connect_async(user="hr", password=userpwd,
dsn="localhost/orclpdb")
cursor = connection.cursor()
await cursor.execute("select user from dual")
async for result in cursor:
print(result)
cursor.close()
await connection.close()
.. _asyncconnpool:
Connection Pools
----------------
Connection pooling allows applications to create and maintain a pool of open
connections to the database. Connection pooling is important for performance
and scalability when applications need to handle a large number of users who do
database work for short periods of time but have relatively long periods when
the connections are not needed. The high availability features of pools also
make small pools useful for applications that want a few connections available
for infrequent use and requires them to be immediately usable when acquired.
An asynchronous connection pool can be created by calling
:meth:`oracledb.create_pool_async()` which returns an :ref:`AsyncConnectionPool
Object <asyncconnpoolobj>`. Note that this method is *synchronous* and does not
use ``await``. Once the pool has been created, your application can get a
connection from it by calling :meth:`AsyncConnectionPool.acquire()`. After
your application has used a connection, it should be released back to the pool
to make it available for other users. This can be done by explicitly closing
the connection or by using an asynchronous context manager, for example:
.. code-block:: python
import asyncio
import oracledb
async def main():
pool = oracle.create_pool_async(user="hr", password=userpwd,
dsn="localhost/orclpdb",
min=1, max=4, increment=1)
async with pool.acquire() as connection:
with connection.cursor() as cursor:
await cursor.execute("select user from dual")
async for result in cursor:
print(result)
await pool.close()
asyncio.run(main())
.. _sqlexecuteasync:
Executing SQL Using Asynchronous Methods
========================================
This section covers executing SQL using the asynchronous programming model.
For discussion of synchronous programming, see :ref:`sqlexecution`.
Your application communicates with Oracle Database by executing SQL
statements. Statements such as queries (statements beginning with SELECT or
WITH), Data Manipulation Language (DML), and Data Definition Language (DDL) are
executed using the asynchronous methods :meth:`AsyncCursor.execute()` or
:meth:`AsyncCursor.executemany()`. Rows can be iterated over, or fetched using
one of the methods :meth:`AsyncCursor.fetchone()`,
:meth:`AsyncCursor.fetchone()`, :meth:`AsyncCursor.fetchmany()`, or
:meth:`AsyncCursor.fetchall()`.
You can also use shortcut methods on the :ref:`asyncconnobj` object such as
:meth:`AsyncConnection.execute()` or
:meth:`AsyncConnection.executemany()`. Rows can be fetched using one of the
shortcut methods :meth:`AsyncConnection.fetchone()`,
:meth:`AsyncConnection.fetchmany()`, or :meth:`AsyncConnection.fetchall()`.
An example of using :meth:`AsyncConnection.fetchall()`:
.. code-block:: python
import asyncio
import oracledb
async def main():
async with oracledb.connect_async(user="hr", password=userpwd,
dsn="localhost/orclpdb") as connection:
res = await connection.fetchall("select * from locations")
print(res)
asyncio.run(main())
An example that uses asyncio for parallelization and shows the execution of
multiple coroutines:
.. code-block:: python
import asyncio
import oracledb
# Number of coroutines to run
CONCURRENCY = 5
# Query the unique session identifier/serial number combination of a connection
SQL = """SELECT UNIQUE CURRENT_TIMESTAMP AS CT, sid||'-'||serial# AS SIDSER
FROM v$session_connect_info
WHERE sid = SYS_CONTEXT('USERENV', 'SID')"""
# Show the unique session identifier/serial number of each connection that the
# pool opens
async def init_session(connection, requested_tag):
res = await connection.fetchone(SQL)
print(res[0].strftime("%H:%M:%S.%f"), '- init_session with SID-SERIAL#', res[1])
# The coroutine simply shows the session identifier/serial number of the
# connection returned by the pool.acquire() call
async def query(pool):
async with pool.acquire() as connection:
await connection.callproc("dbms_session.sleep", [1])
res = await connection.fetchone(SQL)
print(res[0].strftime("%H:%M:%S.%f"), '- query with SID-SERIAL#', res[1])
async def main():
pool = oracledb.create_pool_async(user="hr", password=userpwd,
dsn="localhost/orclpdb",
min=1, max=CONCURRENCY,
session_callback=init_session)
coroutines = [ query(pool) for i in range(CONCURRENCY) ]
await asyncio.gather(*coroutines)
await pool.close()
asyncio.run(main())
When you run this, you will see that multiple connections (identified by the
unique Session Identifier and Serial Number combination) are opened and are
used by ``query()``. For example::
12:09:29.711525 - init_session with SID-SERIAL# 36-38096
12:09:29.909769 - init_session with SID-SERIAL# 33-56225
12:09:30.085537 - init_session with SID-SERIAL# 14-31431
12:09:30.257232 - init_session with SID-SERIAL# 285-40270
12:09:30.434538 - init_session with SID-SERIAL# 282-32608
12:09:30.730166 - query with SID-SERIAL# 36-38096
12:09:30.933957 - query with SID-SERIAL# 33-56225
12:09:31.115008 - query with SID-SERIAL# 14-31431
12:09:31.283593 - query with SID-SERIAL# 285-40270
12:09:31.457474 - query with SID-SERIAL# 282-32608
Your results may vary depending how fast your environment is.
See `async_gather.py <https://github.com/oracle/python-oracledb/tree/main/
samples/async_gather.py>`__ for a runnable example.
.. _txnasync:
Managing Transactions Using Asynchronous Methods
================================================
This section covers managing transactions using the asynchronous programming
model. For discussion of synchronous programming, see :ref:`txnmgmnt`.
When :meth:`AsyncCursor.execute()` or :meth:`AsyncCursor.executemany()`
executes a SQL statement, a transaction is started or continued. By default,
python-oracledb does not commit this transaction to the database. The methods
:meth:`AsyncConnection.commit()` and :meth:`AsyncConnection.rollback()`
methods can be used to explicitly commit or rollback a transaction:
.. code-block:: python
async def main():
async with oracledb.connect_async(user="hr", password=userpwd,
dsn="localhost/orclpdb") as connection:
with connection.cursor as cursor:
await cursor.execute("INSERT INTO mytab (name) VALUES ('John')")
await connection.commit()
When a database connection is closed, such as with
:meth:`AsyncConnection.close()`, or when variables referencing the connection
go out of scope, any uncommitted transaction will be rolled back.
An alternative way to commit is to set the attribute
:attr:`AsyncConnection.autocommit` of the connection to ``True``. This
ensures all :ref:`DML <dml>` statements (INSERT, UPDATE, and so on) are
committed as they are executed.
Note that irrespective of the autocommit value, Oracle Database will always
commit an open transaction when a DDL statement is executed.
When executing multiple DML statements that constitute a single transaction, it
is recommended to use autocommit mode only for the last DML statement in the
sequence of operations. Unnecessarily committing causes extra database load,
and can destroy transactional consistency.

View File

@ -102,11 +102,6 @@ since numeric data is already stored efficiently. Since python-oracledb
allocates memory for each row based on the supplied values, do not oversize
them.
If the size of the buffers allocated for any of the parameters exceeds 2 GB,
you will receive the error ``DPI-1015: array size of <n> is too large``, where
<n> varies with the size of each element being allocated in the buffer. If you
receive this error, decrease the number of elements in the sequence parameters.
Batch Execution of PL/SQL
=========================
@ -311,42 +306,3 @@ be beneficial.
See `load_csv.py <https://github.com/oracle/python-oracledb/tree/main/
samples/load_csv.py>`__ for a runnable example.
Copying Data between Databases
==============================
The :meth:`Cursor.executemany()` function is useful for efficiently copying
data from one database to another:
.. code-block:: python
# Connect to both databases
source_connection = oracledb.connect(user=un1, password=pw1, dsn=cs1)
target_connection = oracledb.connect(user=un2, password=pw2, dsn=cs2)
# Setup cursors
source_cursor = source_connection.cursor()
source_cursor.arraysize = 1000 # tune this for query performance
target_cursor = target_connection.cursor()
target_cursor.setinputsizes(None, 25) # set according to column types
# Perform bulk fetch and insertion
source_cursor.execute("select c1, c2 from MySrcTable")
while True:
rows = source_cursor.fetchmany()
if not rows:
break
target_cursor.executemany("insert into MyDestTable values (:1, :2)", rows)
target_connection.commit()
Tune the :attr:`~Cursor.arraysize` value according to notes in
:ref:`tuningfetch`. Use ``setinputsizes()`` according to `Predefining Memory
Areas`_.
Note that it may be preferable to create a `database link
<https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-D966642A-B19E-449D-9968-1121AF06D793>`__
between the databases and use an INSERT INTO SELECT statement so that data is
not copied to, and back from, the Python process.

View File

@ -6,124 +6,71 @@ Using Bind Variables
SQL and PL/SQL statements that pass data to and from Oracle Database should use
placeholders in SQL and PL/SQL statements that mark where data is supplied or
returned. A bind variable placeholder is a colon-prefixed identifier or
numeral. For example, ``:dept_id`` and ``:dept_name`` are the two bind variable
placeholders in this SQL statement:
returned. These placeholders are referred to as bind variables or bind
parameters. A bind variable is a colon-prefixed identifier or numeral. For
example, there are two bind variables (``dept_id`` and ``dept_name``) in this
SQL statement:
.. code-block:: python
sql = """insert into departments (department_id, department_name)
values (:dept_id, :dept_name)"""
values (:dept_id, :dept_name)"""
cursor.execute(sql, [280, "Facility"])
As part of execution, the supplied bind variable values ``280`` and
``"Facility"`` are substituted for the placeholders by the database. This is
called binding.
Using bind variables is important for scalability and security. They help avoid
SQL Injection security problems because data is never treated as part of an
executable statement when it is parsed.
executable statement. Never concatenate or interpolate user data into SQL
statements:
.. code-block:: python
did = 280
dnm = "Facility"
# !! Never do this !!
sql = f"""insert into departments (department_id, department_name)
values ({did}, '{dnm}')"""
cursor.execute(sql)
Bind variables reduce parsing and execution costs when statements are executed
more than once with different data values. If you do not use bind variables,
Oracle must reparse and cache multiple statements. When using bind variables,
Oracle Database may be able to reuse the statement execution plan and context.
.. note::
Never concatenate or interpolate user data into SQL statements:
.. code-block:: python
did = 280
dnm = "Facility"
# !! Never do this !!
sql = f"""insert into departments (department_id, department_name)
values ({did}, '{dnm}')"""
cursor.execute(sql)
This is a security risk and can impact performance and scalability.
Bind variables can be used to substitute data, but cannot be used to substitute
the text of the statement. You cannot, for example, use a bind variable
placeholder where a column name or a table name is required. Bind variable
placeholders also cannot be used in Data Definition Language (DDL) statements,
such as CREATE TABLE or ALTER statements.
the text of the statement. You cannot, for example, use a bind variable where
a column name or a table name is required. Bind variables also cannot be used
in Data Definition Language (DDL) statements, such as CREATE TABLE or ALTER
statements.
Binding by Name or Position
===========================
Binding can be done "by name" or "by position".
Bind by Name
------------
A named bind is performed when the bind variables in the Python statement use
the names of placeholders in the SQL or PL/SQL statement. For example:
Binding can be done by name or by position. A named bind is performed when the
bind variables in a statement are associated with a name. For example:
.. code-block:: python
cursor.execute("""
insert into departments (department_id, department_name)
values (:dept_id, :dept_name)""",
dept_id=280, dept_name="Facility")
Alternatively, the parameters can be passed as a dictionary instead of as
keyword parameters:
.. code-block:: python
values (:dept_id, :dept_name)""", dept_id=280,
dept_name="Facility")
# alternatively, the parameters can be passed as a dictionary instead of as
# keyword parameters
data = dict(dept_id=280, dept_name="Facility")
cursor.execute("""
insert into departments (department_id, department_name)
values (:dept_id, :dept_name)""", data)
In the above examples, the keyword parameter names or the keys of the
dictionary must match the bind variable placeholder names.
In the above example, the keyword parameter names or the keys of the dictionary
must match the bind variable names. The advantages of this approach are that
the location of the bind variables in the statement is not important, the
names can be meaningful and the names can be repeated while still only
supplying the value once.
The advantages of named binding are that the order of the bind values in the
``execute()`` parameter is not important, the names can be meaningful, and the
placeholder names can be repeated while still only supplying the value once in
the application.
An example of reusing a bind variable placeholder is:
.. code-block:: python
cursor.execute("""
update departments set department_id = :dept_id + 10
where department_id = :dept_id""",
dept_id=280)
Bind by Position
----------------
Positional binding occurs when a list or tuple of bind values is passed to the
``execute()`` call. For example:
.. code-block:: python
cursor.execute("""
insert into departments (department_id, department_name)
values (:1, :2)""", [280, "Facility"])
The following example (which changes the order of the bind placeholder names)
has exactly the same behavior. The value used to substitute the placeholder
":2" will be the first element of the list and ":1" will be replaced by the
second element. Bind by position works from left to right and pays no
attention to the name of the bind variable:
.. code-block:: python
cursor.execute("""
insert into departments (department_id, department_name)
values (:2, :1)""", [280, "Facility"])
The following example is also bind by position despite the bind placeholders
having alphabetic names. The actual process of binding uses the list positions
of the input data to associate the data with the placeholder locations:
A positional bind is performed when a list of bind values are passed to the
execute() call. For example:
.. code-block:: python
@ -131,25 +78,12 @@ of the input data to associate the data with the placeholder locations:
insert into departments (department_id, department_name)
values (:dept_id, :dept_name)""", [280, "Facility"])
Python tuples can also be used for binding by position:
.. code-block:: python
cursor.execute("""
insert into departments (department_id, department_name)
values (:1, :2)""", (280, "Facility"))
If only a single bind placeholder is used in the SQL or PL/SQL statement, the
data can be a list like ``[280]`` or a single element tuple like ``(280,)``.
When using bind by position for SQL statements, the order of the bind values
must exactly match the order of each bind variable and duplicated names must
have their values repeated. For PL/SQL statements, however, the order of the
bind values must exactly match the order of each **unique** bind variable found
in the PL/SQL block and values should not be repeated. In order to avoid this
difference, binding by name is recommended when bind variable names are
repeated.
Note that for SQL statements, the order of the bind values must exactly match
the order of each bind variable and duplicated names must have their values
repeated. For PL/SQL statements, however, the order of the bind values must
exactly match the order of each **unique** bind variable found in the PL/SQL
block and values should not be repeated. In order to avoid this difference,
binding by name is recommended when bind variable names are repeated.
Bind Direction
@ -794,12 +728,11 @@ variable. For example, to use two values in an IN clause:
.. code-block:: python
items = ["Smith", "Taylor"]
cursor.execute("""
select employee_id, first_name, last_name
from employees
where last_name in (:1, :2)""",
items)
select employee_id, first_name, last_name
from employees
where last_name in (:name1, :name2)""",
name1="Smith", name2="Taylor")
for row in cursor:
print(row)
@ -818,12 +751,11 @@ used for up to 5 values, the code will be:
.. code-block:: python
items = ["Smith", "Taylor", None, None, None]
cursor.execute("""
select employee_id, first_name, last_name
from employees
where last_name in (:1, :2, :3, :4, :5)""",
items)
select employee_id, first_name, last_name
from employees
where last_name in (:name1, :name2, :name3, :name4, :name5)""",
name1="Smith", name2="Taylor", name3=None, name4=None, name5=None)
for row in cursor:
print(row)
@ -832,55 +764,31 @@ statement like this for a variable number of values, instead of constructing a
unique statement per set of values, allows best reuse of Oracle Database
resources.
If other bind variables are required in the statement, adjust the bind variable
placeholder numbers:
.. code-block:: python
items = ["Smith", "Taylor", None, None, None]
binds = [120] + items
cursor.execute("""
select employee_id, first_name, last_name
from employees
where employee_id > :1
and last_name in (:2, :3, :4, :5, :6)""",
binds)
for row in cursor:
print(row)
If a statement containing WHERE IN is not going to be re-executed or the number
of values is only going to be known at runtime, then a SQL statement can be
built up as follows:
However, if the statement is not going to be re-executed or the number of
values is only going to be known at runtime, then a SQL statement can be built
up as follows:
.. code-block:: python
bind_values = ["Gates", "Marvin", "Fay"]
bind_names = ",".join(":" + str(i + 1) for i in range(len(bind_values)))
sql = f"select first_name, last_name from employees where last_name in ({bind_names})"
bind_names = [":" + str(i + 1) for i in range(len(bind_values))]
sql = "select employee_id, first_name, last_name from employees " + \
"where last_name in (%s)" % (",".join(bind_names))
cursor.execute(sql, bind_values)
for row in cursor:
print(row)
To exceed the IN clause value limit, you can add OR clauses like:
.. code-block:: python
sql = """select . . .
where key in (:0,:1,:2,:3,:4,:5,:6,:7,:8,:9,:10,:11,...)
or key in (:50,:51,:52,:53,:54,:55,:56,:57,:58,:59,...)
or key in (:100,:101,:102,:103,:104,:105,:106,:107,:108,:109,...)"""
A general solution for a larger number of values is to construct a SQL
statement like::
SELECT ... WHERE col IN ( <something that returns a list of values> )
The best way to do the '<something that returns a list of values>' depends on
how the data is initially represented and the number of items. For example you
might look at using a global temporary table.
The best way to do the '<something that returns a list of values>' depends
on how the data is initially represented and the number of items. You might
look at using CONNECT BY or at using a global temporary table.
One method for up to 32K values is to use an Oracle collection with the
``TABLE()`` clause. For example, if the following type was created::
One method is to use an Oracle collection with the ``TABLE()`` clause. For
example, if the following type was created::
SQL> CREATE OR REPLACE TYPE name_array AS TABLE OF VARCHAR2(25);
2 /
@ -899,9 +807,8 @@ then the application could do:
for row in cursor:
print(row)
For efficiency, retain the return value of ``gettype()`` for reuse where
possible instead of making repeated calls to get the type information.
For efficiency, retain the return value of ``gettype()`` for reuse instead of
making repeated calls to get the type information.
Binding Column and Table Names
==============================
@ -925,15 +832,12 @@ ORDER BY clause:
.. code-block:: python
sql = """
select *
from departments
order by
case :bindvar
when 'DEPARTMENT_ID' then
department_id
else
manager_id
end"""
SELECT * FROM departments
ORDER BY
CASE :bindvar
WHEN 'department_id' THEN DEPARTMENT_ID
ELSE MANAGER_ID
END"""
col_name = get_column_name() # Obtain a column name from the user
cursor.execute(sql, [col_name])

View File

@ -16,9 +16,6 @@ Oracle Client libraries are used. See :ref:`enablingthick`. Both modes have
comprehensive functionality supporting the Python Database API v2.0
Specification.
This chapter covers python-oracledb's synchronous programming model. For
discussion of asynchronous programming, see :ref:`asyncio`.
If you intend to use the Thick mode, then you *must* call
:func:`~oracledb.init_oracle_client()` in the application before any standalone
connection or pool is created. The python-oracledb Thick mode loads Oracle
@ -507,7 +504,7 @@ attributes. For example, to get the stored host name:
Attributes such as the password are not gettable.
You can set individual default attributes using :meth:`ConnectParams.set()`:
You can set individual attributes using :meth:`ConnectParams.set()`:
.. code-block:: python
@ -519,9 +516,6 @@ You can set individual default attributes using :meth:`ConnectParams.set()`:
# change both the port and service name
cp.set(port=1523, service_name="orclpdb")
Note :meth:`ConnectParams.set()` has no effect after
:meth:`ConnectParams.parse_connect_string()` has been called.
Some values such as the database host name can be specified as
:func:`oracledb.connect()`, parameters, as part of the connect string, and in
the ``params`` object. If a ``dsn`` is passed, the python-oracledb :ref:`Thick
@ -534,21 +528,6 @@ that values in any ``dsn`` parameter override values passed as individual
parameters, which themselves override values set in the ``params`` object.
Similar precedence rules also apply to other values.
The :meth:`ConnectParams.parse_dsn_with_credentials()` can be used to extract
the username, password and connection string from a DSN:
.. code-block:: python
cp = oracledb.ConnectParams()
(un,pw,cs) = cp.parse_dsn_with_credentials("scott/tiger@localhost/orclpdb")
Empty values are returned as ``None``.
When creating a standalone connection or connection pool the equivalent
internal extraction is done automatically when a value is passed to the ``dsn``
parameter of :meth:`oracledb.connect()` or :meth:`oracledb.create_pool()` but
no value is passed to the ``user`` password.
.. _connpooling:
Connection Pooling
@ -685,18 +664,14 @@ initiates pool growth will return after the first new connection is created,
regardless of how big ``increment`` is. The pool will then continue to
re-establish connections in a background thread.
A connection pool can shrink back to its minimum size ``min`` when connections
opened by the pool are not used by the application. This frees up database
resources while allowing pools to retain connections for active users. If
connections are idle in the pool (i.e. not currently acquired by the
application) and are unused for longer than the pool creation attribute
``timeout`` value, then they will be closed. The check occurs every
``timeout`` interval and hence in the worst case it may take twice the
``timeout`` time to close the idle connections. The default ``timeout`` is 0
seconds signifying an infinite time and meaning idle connections will never be
closed.
In python-oracledb Thick mode, the pool creation parameter
A connection pool can shrink back to its minimum size when connections opened
by the pool are not used by the application. This frees up database resources
while allowing pools to retain connections for active users. Note this is
currently applicable to Thick mode only. If connections are idle in the pool
(i.e. not currently acquired by the application) and are unused for longer than
the pool creation attribute ``timeout`` value , then they will be closed. The
default ``timeout`` is 0 seconds signifying an infinite time and meaning idle
connections will never be closed. The pool creation parameter
``max_lifetime_session`` also allows pools to shrink. This parameter bounds
the total length of time that a connection can exist starting from the time the
pool created it. If a connection was created ``max_lifetime_session`` or
@ -827,7 +802,7 @@ can be set directly, for example:
.. _sessioncallback:
Session Callbacks for Setting Pooled Connection State
Session CallBacks for Setting Pooled Connection State
-----------------------------------------------------
Applications can set "session" state in each connection. Examples of session
@ -1222,44 +1197,38 @@ Coding Applications to use DRCP
To use DRCP, application connection establishment must request a DRCP pooled
server. The best practice is also to specify a user-chosen connection class
name when creating a connection pool. A 'purity' of the connection session
state can optionally be specified. See the Oracle Database documentation on
`benefiting from scalability <https://www.oracle.com/pls/topic/lookup?ctx=
dblatest&id=GUID-661BB906-74D2-4C5D-9C7E-2798F76501B3>`__ for more information
on purity and connection classes.
name. A 'purity' of the connection session state can optionally be specified.
See the Oracle Database documentation on `benefiting from scalability
<https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-661BB906-74D2-4C5D-9C7E-2798F76501B3>`__
for more information on purity and connection classes.
**Requesting a Pooled Server**
To request the database, use a DRCP pooled server and you can use a specific
connection string in :meth:`oracledb.create_pool()` or
:meth:`oracledb.connect()` like one of the following syntaxes.
To request a DRCP pooled server, you can:
For example with the :ref:`Easy Connect syntax <easyconnect>`:
- Use a specific connection string in :meth:`oracledb.create_pool()` or
:meth:`oracledb.connect()`. For example with the
:ref:`Easy Connect syntax <easyconnect>`:
.. code-block:: python
.. code-block:: python
dsn = "dbhost.example.com/orcl:pooled"
pool = oracledb.create_pool(user="hr", password=userpwd, dsn="dbhost.example.com/orclpdb:pooled",
min=2, max=5, increment=1,
cclass="MYAPP")
- Alternatively, add ``(SERVER=POOLED)`` to the connect descriptor such as
used in an Oracle Network configuration file ``tnsnames.ora``::
Alternatively, add ``(SERVER=POOLED)`` to the connect descriptor such as used in
an Oracle Network configuration file ``tnsnames.ora``::
customerpool = (DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)
(HOST=dbhost.example.com)
(PORT=1521))(CONNECT_DATA=(SERVICE_NAME=CUSTOMER)
(SERVER=POOLED)))
- Another way to use a DRCP pooled server is to set the ``server_type``
parameter during standalone connection creation or python-oracledb
connection pool creation. For example:
You can also specify to use a DRCP pooled server by setting the ``server_type``
parameter when creating a standalone connection or creating a python-oracledb
connection pool. For example:
.. code-block:: python
.. code-block:: python
pool = oracledb.create_pool(user="hr", password=userpwd, dsn="dbhost.example.com/orclpdb",
min=2, max=5, increment=1,
server_type="pooled",
cclass="MYAPP")
server_type="pooled")
**DRCP Connection Class Names**
@ -1272,12 +1241,8 @@ processes are started. A class name also allows better DRCP usage tracking in
the database. In the database monitoring views, the class name shown will be
the value specified in the application prefixed with the user name.
If ``cclass`` was not specified during pool creation, then the python-oracledb
Thin mode generates a unique connection class with the prefix "DPY" while the
Thick mode generates a unique connection class with the prefix "OCI".
To create a connection pool requesting a DRCP pooled server and specifying a
class name, you can call:
class name you can call:
.. code-block:: python
@ -1285,19 +1250,12 @@ class name, you can call:
min=2, max=5, increment=1,
cclass="MYAPP")
Once the pool has been created, your application can get a connection from it
by calling:
.. code-block:: python
connection = pool.acquire()
The python-oracledb connection pool size does not need to match the DRCP pool
size. The limit on overall execution parallelism is determined by the DRCP
pool size.
Connection class names can also be passed to :meth:`~ConnectionPool.acquire()`,
if you want to use a connection with a different class:
if desired:
.. code-block:: python
@ -1335,22 +1293,21 @@ allocated each time :meth:`~ConnectionPool.acquire()` is called:
**Setting the Connection Class and Purity in the Connection String**
Using python-oracledb Thin mode with Oracle Database 21c, you can specify the
class and purity in the connection string itself. This removes the need to
modify an existing application when you want to use DRCP:
For the python-oracledb Thin mode, you can specify the class and purity in the
connection string itself. This removes the need to modify an existing
application when you want to use DRCP:
.. code-block:: python
dsn = "localhost/orclpdb:pooled?pool_connection_class=MYAPP&pool_purity=self"
For python-oracledb Thick mode, this syntax is supported if you are using
Oracle Database 21c and Oracle Client 19c (or later). However, explicitly
specifying the purity as *SELF* in this way may cause some unusable connections
in a python-oracledb Thick mode connection pool to not be terminated. In
summary, if you cannot programmatically set the class name and purity, or
cannot use python-oracledb Thin mode, then avoid explicitly setting the purity
as a connection string parameter when using a python-oracledb connection
pooling in Thick mode.
Recent versions of Oracle Client libraries also support this syntax. However,
explicitly specifying the purity as SELF in this way may cause some unusable
connections in a python-oracledb Thick mode connection pool not to be
terminated. In summary, if you cannot programmatically set the class name and
purity, or cannot use python-oracledb Thin mode, then avoid explicitly setting
the purity as a connection string parameter when using a python-oracledb
connection pooling in Thick mode.
**Closing Connections when using DRCP**
@ -1380,10 +1337,6 @@ other users:
. . .
connection.close();
See `drcp_pool.py
<https://github.com/oracle/python-oracledb/tree/main/samples/drcp_pool.py>`__
for a runnable example of DRCP.
.. _monitoringdrcp:
Monitoring DRCP
@ -2054,7 +2007,7 @@ alternatively, you can specify it inside a connect descriptor stored in
(ADDRESS=(PROTOCOL=TCPS)(PORT=1522)(HOST=xxx.oraclecloud.com))
(CONNECT_DATA=(SERVICE_NAME=xxx.adb.oraclecloud.com))
(SECURITY =
(SSL_SERVER_CERT_DN="CN=xxx.oraclecloud.com, \
(SSL_SERVER_CERT_DN="CN=xxx.oraclecloud.com,OU=Oracle BMCS US, \
O=Oracle Corporation,L=Redwood City,ST=California,C=US")
(TOKEN_AUTH=OAUTH)
(TOKEN_LOCATION="/home/user1/mytokens/oauthtoken")
@ -2292,7 +2245,7 @@ Alternatively, you can specify it in a connect descriptor, for example::
(ADDRESS=(PROTOCOL=TCPS)(PORT=1522)(HOST=xxx.oraclecloud.com))
(CONNECT_DATA=(SERVICE_NAME=xxx.adb.oraclecloud.com))
(SECURITY =
(SSL_SERVER_CERT_DN="CN=xxx.oraclecloud.com, \
(SSL_SERVER_CERT_DN="CN=xxx.oraclecloud.com,OU=Oracle BMCS US, \
O=Oracle Corporation,L=Redwood City,ST=California,C=US")
(TOKEN_AUTH=OCI_TOKEN)
)
@ -2311,7 +2264,7 @@ sqlnet.ora file or in a connect descriptor stored inside
(ADDRESS=(PROTOCOL=TCPS)(PORT=1522)(HOST=xxx.oraclecloud.com))
(CONNECT_DATA=(SERVICE_NAME=xxx.adb.oraclecloud.com))
(SECURITY =
(SSL_SERVER_CERT_DN="CN=xxx.oraclecloud.com, \
(SSL_SERVER_CERT_DN="CN=xxx.oraclecloud.com,OU=Oracle BMCS US, \
O=Oracle Corporation,L=Redwood City,ST=California,C=US")
(TOKEN_AUTH=OCI_TOKEN)
(TOKEN_LOCATION="/path/to/token/folder")
@ -2358,28 +2311,9 @@ This is equivalent to executing the following in SQL*Plus:
.. code-block:: sql
CONNECT sys/syspwd@dbhost.example.com/orclpdb AS SYSDBA
CONNECT sys/syspwd AS SYSDBA
GRANT SYSOPER TO hr;
In python-oracledb Thick mode, when python-oracledb uses Oracle Client
libraries from a database software installation, you can use "bequeath"
connections to databases that are also using the same libraries. Do this by
setting the standard Oracle environment variables such as ``ORACLE_HOME`` and
``ORACLE_SID`` and connecting in Python like:
.. code-block:: python
oracledb.init_oracle_client()
conn = oracledb.connect(mode=oracledb.SYSDBA)
This is equivalent to executing the following in SQL*Plus:
.. code-block:: sql
CONNECT / AS SYSDBA
.. _netencrypt:
Securely Encrypting Network Traffic to Oracle Database
@ -2446,37 +2380,11 @@ requirements and read the documentation for your Oracle version. In particular,
review the available algorithms for security and performance.
The ``NETWORK_SERVICE_BANNER`` column of the database view
`V$SESSION_CONNECT_INFO <https://www.oracle.com/pls/topic/lookup?ctx=dblatest&
`V$SESSION_CONNECT_INFO
<https://www.oracle.com/pls/topic/lookup?ctx=dblatest&
id=GUID-9F0DCAEA-A67E-4183-89E7-B1555DC591CE>`__ can be used to verify the
encryption status of a connection. For example with SQL*Plus::
encryption status of a connection.
SQL> SELECT network_service_banner FROM v$session_connect_info;
If the connection is encrypted, then this query prints an output that includes
the available encryption service, the crypto-checksumming service, and the
algorithms in use, such as::
NETWORK_SERVICE_BANNER
-------------------------------------------------------------------------------------
TCP/IP NT Protocol Adapter for Linux: Version 19.0.0.0.0 - Production
Encryption service for Linux: Version 19.0.1.0.0 - Production
AES256 Encryption service adapter for Linux: Version 19.0.1.0.0 - Production
Crypto-checksumming service for Linux: Version 19.0.1.0.0 - Production
SHA256 Crypto-checksumming service adapter for Linux: Version 19.0.1.0.0 - Production
If the connection is unencrypted, then the query will only print the
available encryption and crypto-checksumming services in the output. For example::
NETWORK_SERVICE_BANNER
-------------------------------------------------------------------------------------
TCP/IP NT Protocol Adapter for Linux: Version 19.0.0.0.0 - Production
Encryption service for Linux: Version 19.0.1.0.0 - Production
Crypto-checksumming service for Linux: Version 19.0.1.0.0 - Production
For more information about Oracle Data Network Encryption and Integrity,
and for information about configuring TLS network encryption, refer to
the `Oracle Database Security Guide <https://www.oracle.com/pls/topic/
lookup?ctx=dblatest&id=DBSEG>`__.
Resetting Passwords
===================
@ -2572,7 +2480,7 @@ ADMIN user:
cs = '''(description = (retry_count=20)(retry_delay=3)(address=(protocol=tcps)
(port=1522)(host=xxx.oraclecloud.com))(connect_data=(service_name=xxx.adb.oraclecloud.com))
(security=(ssl_server_dn_match=yes)(ssl_server_cert_dn="CN=xxx.oraclecloud.com,
(security=(ssl_server_dn_match=yes)(ssl_server_cert_dn="CN=xxx.oraclecloud.com,OU=Oracle BMCS US,
O=Oracle Corporation, L=Redwood City, T=California, C=US")))'''
connection = oracledb.connect(user="admin", password=pw, dsn=cs)
@ -2696,7 +2604,7 @@ to two or more Oracle Autonomous Databases, move each ``cwallet.sso`` file to
its own directory. For each connection use different connection string
``WALLET_LOCATION`` parameters to specify the directory of each ``cwallet.sso``
file. It is recommended to use Oracle Client libraries 19.17 (or later) when
using :ref:`multiple wallets <connmultiwallets>`.
using multiple wallets.
Access Through a Proxy
+++++++++++++++++++++++
@ -2765,15 +2673,15 @@ For example, if your ``tnsnames.ora`` file had an entry::
cjjson_high = (description=(retry_count=20)(retry_delay=3)
(address=(protocol=tcps)(port=1522)
(host=xxx.oraclecloud.com))
(host=adb.ap-sydney-1.oraclecloud.com))
(connect_data=(service_name=abc_cjjson_high.adb.oraclecloud.com))
(security=(ssl_server_cert_dn="CN=xxx.oraclecloud.com,O=Oracle Corporation,L=Redwood City,ST=California,C=US")))
(security=(ssl_server_cert_dn="CN=adb.ap-sydney-1.oraclecloud.com,OU=Oracle ADB SYDNEY,O=Oracle Corporation,L=Redwood City,ST=California,C=US")))
Then your applications can connect using the connection string:
.. code-block:: python
dsn = "tcps://xxx.oraclecloud.com:1522/abc_cjjson_high.adb.oraclecloud.com?wallet_location=/Users/cjones/Cloud/CJJSON&retry_count=20&retry_delay=3"
dsn = "tcps://adb.ap-sydney-1.oraclecloud.com:1522/abc_cjjson_high.adb.oraclecloud.com?wallet_location=/Users/cjones/Cloud/CJJSON&retry_count=20&retry_delay=3"
connection = oracledb.connect(user="hr", password=userpwd, dsn=dsn)
The ``wallet_location`` parameter needs to be set to the directory containing
@ -2864,60 +2772,6 @@ location as the ``wallet_location`` parameter to :func:`oracledb.connect()` or
f.write(cert.public_bytes(Encoding.PEM))
print("PEM file", pem_file_name, "written.")
.. _connmultiwallets:
Connecting using Multiple Wallets
=================================
You can make multiple connections with different wallets in one Python
process.
**In python-oracledb Thin mode**
To use multiple wallets in python-oracledb Thin mode, pass the different
connection strings, wallet locations, and wallet password (if required) in each
:meth:`oracledb.connect()` call or when creating a :ref:`connection pool
<connpooling>`:
.. code-block:: python
connection = oracledb.connect(user=user_name, password=userpw, dsn=dsn,
config_dir="path_to_extracted_wallet_zip",
wallet_location="location_of_pem_file",
wallet_password=walletpw)
The ``config_dir`` parameter is the directory containing the :ref:`tnsnames.ora
<optnetfiles>` file. The ``wallet_location`` parameter is the directory
containing the ``ewallet.pem`` file. If you are using Oracle Autonomous
Database, both of these paths are typically the same directory where the
``wallet.zip`` file was extracted. The ``dsn`` should specify a TCPS
connection.
**In python-oracledb Thick mode**
To use multiple wallets in python-oracledb Thick mode, a TCPS connection string
containing the ``MY_WALLET_DIRECTORY`` option needs to be created:
.. code-block:: python
dsn = "mydb_high" # one of the network aliases from tnsnames.ora
params = oracledb.ConnectParams(config_dir="path_to_extracted_wallet_zip",
wallet_location="path_location_of_sso_file")
params.parse_connect_string(dsn)
dsn = params.get_connect_string()
connection = oracledb.connect(user=user_name, password=password, dsn=dsn)
The ``config_dir`` parameter should be the directory containing the
:ref:`tnsnames.ora <optnetfiles>` and ``sqlnet.ora`` files. The
``wallet_location`` parameter is the directory containing the ``cwallet.sso``
file. If you are using Oracle Autonomous Database, both of these paths are
typically the same directory where the ``wallet.zip`` file was extracted.
.. note::
Use Oracle Client libraries 19.17, or later, or use Oracle Client 21c.
They contain important bug fixes for using multiple wallets in the one
process.
.. _connsharding:

View File

@ -105,19 +105,12 @@ Thin Mode Locale-aware Number and Date Conversions
.. note::
All NLS environment variables are ignored by the python-oracledb Thin mode.
Also the ``ORA_TZFILE`` variable is ignored.
.. note::
Trying to access TIMESTAMP WITH TIME ZONE data that contains a named time
zone will throw ``DPY-3022: named time zones are not supported in thin
mode``. Data stored with a numeric offset such as ``+00:00`` can be
fetched.
Also the ``ORA_SDTZ`` and ``ORA_TZFILE`` variables are ignored.
In the python-oracledb Thin mode, output type handlers need to be used to
perform date and number localizations. The examples below show a simple
conversion and also how the Python locale module can be used. Type handlers
like those below can also be used in python-oracledb Thick mode.
perform similar conversions. The examples below show a simple conversion and
also how the Python locale module can be used. Type handlers like those below
can also be used in python-oracledb Thick mode.
To convert numbers:
@ -133,16 +126,16 @@ To convert numbers:
locale.setlocale(locale.LC_ALL, 'de_DE.UTF-8')
# simple naive conversion
def type_handler1(cursor, metadata):
if metadata.type_code is oracledb.DB_TYPE_NUMBER:
def type_handler1(cursor, name, default_type, size, precision, scale):
if default_type == oracledb.DB_TYPE_NUMBER:
return cursor.var(oracledb.DB_TYPE_VARCHAR, arraysize=cursor.arraysize,
outconverter=lambda v: v.replace('.', ','))
outconverter=lambda v: v.replace('.', ','))
# locale conversion
def type_handler2(cursor, metadata):
if metadata.type_code is oracledb.DB_TYPE_NUMBER:
return cursor.var(metadata.type_code, arraysize=cursor.arraysize,
outconverter=lambda v: locale.format_string("%g", v))
def type_handler2(cursor, name, default_type, size, precision, scale):
if default_type == oracledb.DB_TYPE_NUMBER:
return cursor.var(default_type, arraysize=cursor.arraysize,
outconverter=lambda v: locale.format_string("%g", v))
connection = oracledb.connect(user="hr", password=userpwd,
@ -186,16 +179,16 @@ To convert dates:
locale_date_format = locale.nl_langinfo(locale.D_T_FMT)
# simple naive conversion
def type_handler3(cursor, metadata):
if metadata.type_code is oracledb.DB_TYPE_DATE:
return cursor.var(metadata.type_code, arraysize=cursor.arraysize,
outconverter=lambda v: v.strftime("%Y-%m-%d %H:%M:%S"))
def type_handler3(cursor, name, default_type, size, precision, scale):
if default_type == oracledb.DB_TYPE_DATE:
return cursor.var(default_type, arraysize=cursor.arraysize,
outconverter=lambda v: v.strftime("%Y-%m-%d %H:%M:%S"))
# locale conversion
def type_handler4(cursor, name, default_type, size, precision, scale):
if metadata.type_code is oracledb.DB_TYPE_DATE:
return cursor.var(metadata.type_code, arraysize=cursor.arraysize,
outconverter=lambda v: v.strftime(locale_date_format))
if default_type == oracledb.DB_TYPE_DATE:
return cursor.var(default_type, arraysize=cursor.arraysize,
outconverter=lambda v: v.strftime(locale_date_format))
connection = oracledb.connect(user="hr", password=userpwd,

View File

@ -16,103 +16,76 @@ supporting the Python Database API v2.0 Specification.
Enabling python-oracledb Thick mode
===================================
If you are upgrading a cx_Oracle application to python-oracledb, then refer to
Changing from the default Thin mode to the Thick mode requires the addition of
a call to :func:`oracledb.init_oracle_client()` as shown in sections below.
Other small code updates may also be required, see :ref:`driverdiff` . If you are
upgrading a cx_Oracle application to python-oracledb, then refer to
:ref:`upgrading83` for changes that may be needed.
To change from the default Thin mode to the Thick mode:
.. note::
- Oracle Client libraries must be available to handle communication to your
database. These need to be installed separately, see :ref:`installation`.
To use python-oracledb in Thick mode you *must* call
:func:`~oracledb.init_oracle_client()` in the application. It must be
called before any :ref:`standalone connection <standaloneconnection>` or
:ref:`connection pool <connpooling>` is created. If a connection or pool
is first created, then the Thick mode cannot be enabled.
Various versions of Oracle Client libraries can be used. They do not have to
match the version of Oracle Database. Python-oracledb can use the Client
Libraries from:
All connections in an application use the same mode.
- an installation of `Oracle Instant Client
<https://www.oracle.com/database/technologies/instant-client.html>`__
Once the Thick mode is enabled, you cannot go back to Thin mode except by
restarting the application.
- or a full Oracle Client installation (installed by running the Oracle
Universal installer ``runInstaller``)
Enabling the python-oracledb Thick mode loads Oracle Client libraries which
handle communication to your database. The Oracle Client libraries need to be
installed separately. See :ref:`installation`.
- or an Oracle Database installation, if Python is running on the same
machine as the database
.. figure:: /images/python-oracledb-thick-arch.png
:alt: architecture of the python-oracledb driver in Thick mode
- Your application *must* call the function
:meth:`oracledb.init_oracle_client()`. For example, if the Oracle Instant
Client libraries are in ``C:\oracle\instantclient_19_17`` on Windows or
``$HOME/Downloads/instantclient_19_8`` on macOS (Intel x86), then you can
use:
Architecture of the python-oracledb driver in Thick mode
.. code-block:: python
You can validate python-oracledb is running in Thick mode by querying the
``CLIENT_DRIVER`` column of ``V$SESSION_CONNECT_INFO`` and verifying the value
of the column begins with ``python-oracledb thk``. See :ref:`vsessconinfo`.
import os
import platform
import oracledb
.. _libinit:
d = None # default suitable for Linux
if platform.system() == "Darwin" and platform.machine() == "x86_64": # macOS
d = os.environ.get("HOME")+("/Downloads/instantclient_19_8")
elif platform.system() == "Windows":
d = r"C:\oracle\instantclient_19_18"
oracledb.init_oracle_client(lib_dir=d)
Setting the Oracle Client Library Directory
-------------------------------------------
The use of a raw string ``r"..."`` on Windows means that backslashes are
treated as directory separators.
When :meth:`~oracledb.init_oracle_client()` is called, python-oracledb
dynamically loads Oracle Client libraries using a search heuristic. The
libraries can be:
More details and options are shown in the later sections
:ref:`wininit`, :ref:`macinit`, and :ref:`linuxinit`.
- in an installation of Oracle Instant Client
- or in a full Oracle Client installation
- or in an Oracle Database installation (if Python is running on the same
machine as the database).
All connections in an application use the same mode.
See :ref:`installation` for information about installing Oracle Client
libraries.
Once the Thick mode is enabled, you cannot go back to the Thin mode except by
removing calls to :meth:`~oracledb.init_oracle_client()` and restarting the
application.
The versions of Oracle Client libraries and Oracle Database do not have to be
the same. For certified configurations see Oracle Support's `Doc ID 207303.1
<https://support.oracle.com/epmos/faces/DocumentDisplay?id=207303.1>`__.
See :ref:`vsessconinfo` to verify which mode is in use.
.. note::
**Notes on calling init_oracle_client()**
- The :meth:`~oracledb.init_oracle_client()` function must be called before
any :ref:`standalone connection <standaloneconnection>` or
:ref:`connection pool <connpooling>` is created. If a connection or pool
is first created, then the Thick mode cannot be enabled.
- If you call :meth:`~oracledb.init_oracle_client()` with a ``lib_dir`` parameter,
the Oracle Client libraries are loaded immediately from that directory. If
you call :meth:`~oracledb.init_oracle_client()` but do *not* set the ``lib_dir``
parameter, the Oracle Client libraries are loaded immediately using the
search heuristics discussed in later sections.
- If Oracle Client libraries cannot be loaded then
:meth:`~oracledb.init_oracle_client()` will raise an error ``DPI-1047:
Oracle Client library cannot be loaded``. To resolve this, review the
platform-specific instructions below or see :ref:`runtimetroubleshooting`.
Alternatively, remove the call to :meth:`~oracledb.init_oracle_client()` and
use Thin mode. The features supported by Thin mode can be found in
:ref:`driverdiff`.
- If you set ``lib_dir`` on Linux and related platforms, you must still have
configured the system library search path to include that directory before
starting Python.
- On any operating system, if you set ``lib_dir`` to the library directory of a
full database or full client installation (such as from running
``runInstaller``), you will need to have previously set the Oracle environment,
for example by setting the ``ORACLE_HOME`` environment variable. Otherwise you
will get errors like ``ORA-1804``. You should set this variable, and other
Oracle environment variables, before starting Python, as shown in :ref:`Oracle
Environment Variables <envset>`.
- The :meth:`~oracledb.init_oracle_client()` function may be called multiple
times in your application but must always pass the same arguments.
If Oracle Client libraries cannot be loaded then
:meth:`~oracledb.init_oracle_client()` will raise an error ``DPI-1047:
Oracle Client library cannot be loaded``. To resolve this, review the
platform-specific instructions below or see :ref:`runtimetroubleshooting`.
Alternatively remove the call to :meth:`~oracledb.init_oracle_client()` and
use Thin mode. The features supported by Thin mode can be found in
:ref:`driverdiff`.
.. _wininit:
Enabling python-oracledb Thick Mode on Windows
----------------------------------------------
Setting the Oracle Client Library Directory on Windows
++++++++++++++++++++++++++++++++++++++++++++++++++++++
On Windows, the alternative ways to enable Thick mode are:
On Windows, python-oracledb Thick mode can be enabled as follows:
- By passing the ``lib_dir`` parameter in a call to
:meth:`~oracledb.init_oracle_client()`, for example:
@ -121,25 +94,22 @@ On Windows, the alternative ways to enable Thick mode are:
import oracledb
oracledb.init_oracle_client(lib_dir=r"C:\instantclient_19_18")
oracledb.init_oracle_client(lib_dir=r"C:\instantclient_19_14")
On Windows, when the path contains backslashes, use a 'raw' string like
``r"C:\instantclient_19_18"``.
This directory should contain the libraries from an unzipped Instant
Client 'Basic' or 'Basic Light' package. If you pass the library
directory from a full client or database installation, such as Oracle
Database "XE" Express Edition, then you will need to have previously set
your environment to use that same software installation. Otherwise, files
such as message files will not be located and you may have library
version clashes. On Windows, when the path contains backslashes, use a
'raw' string like ``r"C:\instantclient_19_14"``.
This directory should contain the libraries from an unzipped `Instant Client
'Basic' or 'Basic Light' <https://www.oracle.com/au/database/technologies/
instant-client.html>`__ package. If you pass the library directory from a
full client or database installation, such as `Oracle Database “XE” Express
Edition <https://www.oracle.com/database/technologies/appdev/xe.html>`__,
then you will need to have previously set your environment to use that same
software installation. Otherwise, files such as message files will not be
located and you may have library version clashes.
If the Oracle Client libraries cannot be loaded from ``lib_dir``, then an
exception is raised.
If the Oracle Client libraries cannot be loaded, then an exception is
raised.
- Alternatively, you can call :meth:`~oracledb.init_oracle_client()` without
passing a ``lib_dir`` parameter:
- By calling :meth:`~oracledb.init_oracle_client()` without passing a
``lib_dir`` parameter:
.. code-block:: python
@ -147,11 +117,10 @@ On Windows, the alternative ways to enable Thick mode are:
oracledb.init_oracle_client()
In this case, Oracle Client libraries are first looked for in the directory
where the python-oracledb binary module is installed. This directory should
contain the libraries from an unzipped `Instant Client 'Basic' or 'Basic
Light' <https://www.oracle.com/au/database/technologies/instant-client
.html>`__ package.
In this case, Oracle Client libraries are first looked for in the
directory where the python-oracledb binary module is installed. This
directory should contain the libraries from an unzipped Instant Client
'Basic' or 'Basic Light' package.
If the libraries are not found there, the search looks at the directories
on the system library search path, for example, the ``PATH`` environment
@ -162,10 +131,10 @@ On Windows, the alternative ways to enable Thick mode are:
.. _macinit:
Enabling python-oracledb Thick Mode on macOS
--------------------------------------------
Setting the Oracle Client Library Directory on macOS
++++++++++++++++++++++++++++++++++++++++++++++++++++
On macOS, the alternative ways to enable Thick mode are:
On macOS, python-oracledb Thick mode can be enabled as follows:
- By passing the ``lib_dir`` parameter in a call to
:meth:`~oracledb.init_oracle_client()`, for example:
@ -176,26 +145,24 @@ On macOS, the alternative ways to enable Thick mode are:
oracledb.init_oracle_client(lib_dir="/Users/your_username/Downloads/instantclient_19_8")
This directory should contain the libraries from an unzipped `Instant Client
'Basic' or 'Basic Light' <https://www.oracle.com/au/database/technologies/
instant-client.html>`__ package.
This directory should contain the libraries from an unzipped Instant
Client 'Basic' or 'Basic Light' package. If the Oracle Client libraries
cannot be loaded from ``lib_dir``, then an exception is raised.
- Alternatively, you can call :meth:`~oracledb.init_oracle_client()` without
passing a ``lib_dir`` parameter:
- By calling :meth:`~oracledb.init_oracle_client()` without passing a
``lib_dir`` parameter:
.. code-block:: python
.. code-block:: python
import oracledb
import oracledb
oracledb.init_oracle_client()
oracledb.init_oracle_client()
In this case, the Oracle Client libraries are first looked for in the
directory where the python-oracledb Thick mode binary module is installed.
This directory should contain the libraries from an unzipped `Instant Client
'Basic' or 'Basic Light'
<https://www.oracle.com/au/database/technologies/instant-client.html>`__
package, or a symbolic link to the main Oracle Client library if Instant
Client is in a different directory.
This directory should contain the libraries from an unzipped Instant Client
'Basic' or 'Basic Light' package, or a symbolic link to the main Oracle
Client library if Instant Client is in a different directory.
You can find the directory containing the Thick mode binary module by calling
the python CLI without specifying a Python script, executing ``import
@ -225,65 +192,101 @@ On macOS, the alternative ways to enable Thick mode are:
os.symlink(os.path.join(CLIENT_DIR, LIB_NAME),
os.path.join(target_dir, LIB_NAME))
If python-oracledb does not find the Oracle Client library in that directory,
the directories on the system library search path may be used, for example,
``~/lib/`` and ``/usr/local/lib``, or in ``$DYLD_LIBRARY_PATH``. These paths
will vary with macOS version and Python version. Any value in
``DYLD_LIBRARY_PATH`` will not propagate to a sub-shell, so do not rely on
setting it.
If python-oracledb does not find the Oracle Client library in that
directory, the directories on the system library search path may be used,
for example, ``~/lib/`` and ``/usr/local/lib``, or in ``$DYLD_LIBRARY_PATH``.
These paths will vary with macOS version and Python version. Any value
in ``DYLD_LIBRARY_PATH`` will not propagate to a sub-shell.
If the Oracle Client libraries cannot be loaded, then an exception is
raised.
.. _linuxinit:
Setting the Oracle Client Library Directory on Linux and Related Platforms
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
On Linux and related platforms, python-oracledb Thick mode can be enabled as
follows:
- By calling :meth:`~oracledb.init_oracle_client()` without passing a
``lib_dir`` parameter:
.. code-block:: python
import oracledb
oracledb.init_oracle_client()
Oracle Client libraries are looked for in the operating system library
search path, such as configured with ``ldconfig`` or set in the environment
variable ``LD_LIBRARY_PATH``. On some UNIX platforms an OS specific
equivalent, such as ``LIBPATH`` or ``SHLIB_PATH`` is used instead of
``LD_LIBRARY_PATH``.
If libraries are not found in the system library search path, then
``$ORACLE_HOME/lib`` will be used. Note that the environment variable
``ORACLE_HOME`` should only ever be set when you have a full database
installation or full client installation (such as installed with the Oracle
GUI installer). It should not be set if you are using Oracle Instant
Client. The ``ORACLE_HOME`` variable, and other necessary variables, should
be set before starting Python. See :ref:`envset`.
If the Oracle Client libraries cannot be loaded, then an exception is
raised.
Ensure that the Python process has directory and file access permissions for
the Oracle Client libraries.
the Oracle Client libraries. On some platforms OS restrictions may prevent the
opening of Oracle Client libraries installed in unsafe paths, such as from a
user directory. On Linux ensure a ``libclntsh.so`` file exists. On macOS
ensure a ``libclntsh.dylib`` file exists. Python-oracledb Thick mode will not
directly load ``libclntsh.*.XX.1`` files in ``lib_dir`` or from the directory
where the python-oracledb binary module is available. Note that other
libraries used by ``libclntsh*`` are also required.
.. _linuxinit:
.. _usinginitoracleclient:
Enabling python-oracledb Thick Mode on Linux and Related Platforms
------------------------------------------------------------------
Example Calling oracledb.init_oracle_client()
+++++++++++++++++++++++++++++++++++++++++++++
On Linux and related platforms, enable Thick mode by calling
:meth:`~oracledb.init_oracle_client()` without passing a ``lib_dir``
parameter.
Oracle Client Libraries are loaded when :meth:`oracledb.init_oracle_client()`
is called. In some environments, applications can use the ``lib_dir``
parameter to specify the directory containing the Oracle Client libraries.
Otherwise, the system library search path should contain the relevant library
directory before Python is invoked.
For example, if the Oracle Instant Client Libraries are in
``C:\oracle\instantclient_19_17`` on Windows or
``$HOME/Downloads/instantclient_19_8`` on macOS (Intel x86), then you can use:
.. code-block:: python
import oracledb
import os
import platform
oracledb.init_oracle_client()
d = None # default suitable for Linux
if platform.system() == "Darwin" and platform.machine() == "x86_64":
d = os.environ.get("HOME")+"/Downloads/instantclient_19_8")
elif platform.system() == "Windows":
d = r"C:\oracle\instantclient_19_17"
oracledb.init_oracle_client(lib_dir=d)
Oracle Client libraries are looked for in the operating system library search
path, such as configured with ``ldconfig`` or set in the environment variable
``LD_LIBRARY_PATH``. Web servers and other daemons commonly reset environment
variables so using ``ldconfig`` is generally preferred instead. On some UNIX
platforms an OS specific equivalent, such as ``LIBPATH`` or ``SHLIB_PATH``, is
used instead of ``LD_LIBRARY_PATH``.
The use of a 'raw' string ``r"..."`` on Windows means that backslashes are
treated as directory separators.
If libraries are not found in the system library search path, then libraries
in ``$ORACLE_HOME/lib`` will be used. Note that the environment variable
``ORACLE_HOME`` should only ever be set when you have a full database
installation or full client installation (such as installed with the Oracle
GUI installer). It should not be set if you are using `Oracle Instant Client
<https://www.oracle.com/au/database/technologies/instant-client.html>`__. If
being used, ``ORACLE_HOME`` and other necessary Oracle environment variables
should be set before starting Python. See :ref:`envset`.
**Note that if you set** ``lib_dir`` **on Linux and related platforms, you must
still have configured the system library search path to include that directory
before starting Python**.
If the Oracle Client libraries cannot be loaded, then an exception is
raised.
On any operating system, if you set ``lib_dir`` to the library directory of a
full database or full client installation, you will need to have previously set
the Oracle environment, for example by setting the ``ORACLE_HOME`` environment
variable. Otherwise, you will get errors like ``ORA-1804``. You should set this
along with other Oracle environment variables before starting Python as
shown in :ref:`envset`.
On Linux, python-oracledb Thick mode will not automatically load Oracle Client
library files from the directory where the python-oracledb binary module is
located. One of the above methods should be used instead.
Ensure that the Python process has directory and file access permissions for
the Oracle Client libraries. OS restrictions may prevent the opening of Oracle
Client libraries installed in unsafe paths, such as from a user directory. You
may need to install the Oracle Client libraries under a directory like ``/opt``
or ``/usr/local``.
Tracing Oracle Client Libraries Loading
---------------------------------------
**Tracing Oracle Client Libraries Loading**
To trace the loading of Oracle Client libraries, the environment variable
``DPI_DEBUG_LEVEL`` can be set to 64 before starting Python. At a Windows
@ -295,18 +298,12 @@ On Linux and macOS, you might use::
export DPI_DEBUG_LEVEL=64
When your python-oracledb application is run, logging output is shown on the
terminal.
.. _optconfigfiles:
Optional Oracle Configuration Files
===================================
When your python-oracledb application is run, logging output is shown.
.. _optnetfiles:
Optional Oracle Net Configuration Files
---------------------------------------
=======================================
Optional Oracle Net configuration files may be read by python-oracledb. These
files affect connections and applications. The common files are:
@ -322,30 +319,109 @@ files affect connections and applications. The common files are:
<https://www.oracle.com/pls/topic/lookup?ctx=dblatest&
id=GUID-19423B71-3F6C-430F-84CC-18145CC2A818>`__ for more information.
The ``sqlnet.ora`` file is only used in the python-oracledb Thick mode. See
:ref:`enablingthick`. In the python-oracledb Thin mode, many of the
equivalent settings can be defined as connection time parameters, for
example by using the :ref:`ConnectParams Class <connparam>`.
.. note::
See :ref:`usingconfigfiles` to understand how python-oracledb locates the
files.
The ``sqlnet.ora`` file is only supported in the python-oracledb Thick
mode. See :ref:`enablingthick`.
In the python-oracledb Thin mode, many of the equivalent settings can be
defined as connection time parameters, for example by using the
:ref:`ConnectParams Class <connparam>`.
**python-oracledb Thin mode**
In python-oracledb Thin mode applications, you specify the directory that
contains the ``tnsnames.ora`` file by:
- setting the `TNS_ADMIN
<https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-12C94B15-2CE1-4B98-9D0C-8226A9DDF4CB>`__
environment variable to the directory containing the file
- setting :attr:`defaults.config_dir` to the directory containing the file
- setting the ``config_dir`` parameter to the directory containing the file
when :func:`connecting <oracledb.connect()>` or creating a
:func:`connection pool <oracledb.create_pool()>`.
For example:
.. code-block:: python
import oracledb
oracledb.defaults.config_dir = "/opt/oracle/config"
.. note::
In Thin mode, you must explicitly set the directory because traditional
"default" locations such as the Instant Client ``network/admin/``
subdirectory, or ``$ORACLE_HOME/network/admin/``, or
``$ORACLE_BASE/homes/XYZ/network/admin/`` (in a read-only Oracle Database
home) are not automatically looked in.
**python-oracledb Thick mode**
In python-oracledb Thick mode, the files are loaded from default locations
(shown below), from the directory also specified in the ``$TNS_ADMIN``
environment variable, or from the directory specified as a parameter in the
:meth:`oracledb.init_oracle_client()` call. For example, if the file
``/opt/oracle/config/tnsnames.ora`` should be used, you can call:
.. code-block:: python
import oracledb
import sys
try:
oracledb.init_oracle_client(config_dir="/opt/oracle/config")
except Exception as err:
print("Whoops!")
print(err)
sys.exit(1)
.. note::
In python-oracledb Thick mode, once an application has created its first
connection, trying to change the configuration directory will not have any
effect.
If :meth:`~oracledb.init_oracle_client()` is called to enable Thick mode but
``config_dir`` is not specified, then default directories are searched for the
configuration files. They include:
- ``$TNS_ADMIN``
- ``/opt/oracle/instantclient_19_14/network/admin`` if Instant Client is in
``/opt/oracle/instantclient_19_14``.
- ``/usr/lib/oracle/19.14/client64/lib/network/admin`` if Oracle 19.6 Instant
Client RPMs are used on Linux.
- ``$ORACLE_HOME/network/admin`` if python-oracledb Thick is using libraries
from a database installation.
Note that the :ref:`easyconnect` can set many common configuration options
without needing ``tnsnames.ora`` or ``sqlnet.ora`` files.
The section :ref:`Network Configuration <hanetwork>` has additional information
about Oracle Net configuration.
.. _optclientfiles:
Optional Oracle Client Configuration File
-----------------------------------------
=========================================
When python-oracledb Thick mode uses Oracle Client libraries version 12.1 or
later, an optional client parameter file called ``oraaccess.xml`` can be used
to configure some behaviors of those libraries, such as statement caching and
When python-oracledb uses Oracle Client libraries version 12.1 or later, an
optional client parameter file called ``oraaccess.xml`` can be used to
configure some behaviors of those libraries, such as statement caching and
prefetching. This can be useful if the application cannot be altered. The
file is read from the same directory as the `Optional Oracle Net Configuration
Files`_.
.. note::
The ``oraaccess.xml`` file is only used in the python-oracledb Thick mode.
See :ref:`enablingthick`.
The ``oraaccess.xml`` file is only supported in the python-oracledb Thick
mode. See :ref:`enablingthick`.
A sample ``oraaccess.xml`` file that sets the Oracle client 'prefetch' value to
1000 rows. This value affects every SQL query in the application::
@ -362,120 +438,27 @@ A sample ``oraaccess.xml`` file that sets the Oracle client 'prefetch' value to
</default_parameters>
</oraaccess>
See :ref:`tuningfetch` for information about prefetching.
Prefetching is the number of additional rows that the underlying Oracle Client
library fetches whenever python-oracledb Thick requests query data from the database.
Prefetching is a tuning option to maximize data transfer efficiency and minimize
:ref:`round-trips <roundtrips>` to the database. The prefetch size does not
affect when or how many rows are returned by the Thick mode to the application.
The cache management is transparently handled by the Oracle Client libraries.
Note that standard Thick mode fetch tuning is done using :attr:`Cursor.arraysize`, but
changing the prefetch value can be useful in some cases such as when modifying
the application is not feasible.
The ``oraaccess.xml`` file has other uses including:
The `oraaccess.xml` file has other uses including:
- Changing the value of Fast Application Notification :ref:`FAN <fan>` events
which affects notifications and Runtime Load Balancing (RLB).
- Configuring `Client Result Caching <https://www.oracle.com/pls/topic/lookup?
ctx=dblatest&id=GUID-D2FA7B29-301B-4AB8-8294-2B1B015899F9>`__ parameters.
- Turning on `Client Statement Cache Auto-tuning <https://www.oracle.com/pls/
topic/lookup?ctx=dblatest&id=GUID-75169FE4-DE2C-431F-BBA7-3691C7C33360>`__.
- Changing the value of Fast Application Notification :ref:`FAN <fan>` events which affects notifications and Runtime Load Balancing (RLB).
- Configuring `Client Result Caching <https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-D2FA7B29-301B-4AB8-8294-2B1B015899F9>`__ parameters
- Turning on `Client Statement Cache Auto-tuning <https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-75169FE4-DE2C-431F-BBA7-3691C7C33360>`__
Refer to the documentation on `oraaccess.xml <https://www.oracle.com/pls/topic
/lookup?ctx=dblatest&id=GUID-9D12F489-EC02-46BE-8CD4-5AECED0E2BA2>`__
Refer to the documentation on `oraaccess.xml
<https://www.oracle.com/pls/topic/lookup?
ctx=dblatest&id=GUID-9D12F489-EC02-46BE-8CD4-5AECED0E2BA2>`__
for more details.
See :ref:`usingconfigfiles` to understand how python-oracledb locates the
files.
.. _usingconfigfiles:
Using Optional Oracle Configuration Files
-----------------------------------------
If you use optional Oracle configuration files such as ``tnsnames.ora``,
``sqlnet.ora`` or ``oraaccess.xml``, then put the files in an accessible
directory and follow the Thin or Thick mode instructions below.
The files should be in a directory accessible to Python, not on the database
server host.
**For python-oracledb Thin mode**
In python-oracledb Thin mode, you must specify the directory that contains the
``tnsnames.ora`` file by either:
- Setting the `TNS_ADMIN <https://www.oracle.com/pls/topic/lookup?ctx=dblatest
&id=GUID-12C94B15-2CE1-4B98-9D0C-8226A9DDF4CB>`__ environment variable to the
directory containing the file
- Or setting :attr:`defaults.config_dir` to the directory containing the file.
For example:
.. code-block:: python
import oracledb
oracledb.defaults.config_dir = "/opt/oracle/config"
- Or setting the ``config_dir`` parameter to the directory containing the file
when :func:`connecting <oracledb.connect()>` or creating a
:func:`connection pool <oracledb.create_pool()>`. For example:
.. code-block:: python
connection = oracledb.connect(user="hr", password=userpwd, dsn="orclpdb",
config_dir="/opt/oracle/config")
On Windows, when the path contains backslashes, use a 'raw' string like
``r"C:\instantclient_19_18"``.
.. note::
In Thin mode, you must explicitly set the directory because traditional
"default" locations such as the Instant Client ``network/admin/``
subdirectory, or ``$ORACLE_HOME/network/admin/``, or
``$ORACLE_BASE/homes/XYZ/network/admin/`` (in a read-only Oracle Database
home) are not automatically looked in.
**For python-oracledb Thick mode**
In python-oracledb Thick mode, the directory containing the optional files can
be explicitly specified or a default location will be used. Do one of:
- Set the ``config_dir`` parameter to the directory containing the files
in the :meth:`oracledb.init_oracle_client()` call:
.. code-block:: python
oracledb.init_oracle_client(config_dir="/opt/oracle/config")
On Windows, when the path contains backslashes, use a 'raw' string like
``r"C:\instantclient_19_18"``.
.. note::
In python-oracledb Thick mode, once an application has created its first
connection, trying to change the configuration directory will not have any
effect.
- If :meth:`~oracledb.init_oracle_client()` is called to enable Thick mode but
``config_dir`` is not specified, then default directories are searched for the
configuration files. They include:
- The directory specified by the `TNS_ADMIN <https://www.oracle.com/pls/
topic/lookup?ctx=dblatest&id=GUID-12C94B15-2CE1-4B98-9D0C-8226A9DDF4CB>`__
environment variable.
- For Oracle Instant Client ZIP files, the ``network/admin`` subdirectory of
Instant Client, for example
``/opt/oracle/instantclient_19_18/network/admin``.
- For Oracle Instant RPMs, the ``network/admin`` subdirectory of Instant
Client, for example ``/usr/lib/oracle/19.18/client64/lib/network/admin``.
- When using libraries from a local Oracle Database or full client
installation, in ``$ORACLE_HOME/network/admin`` or
``$ORACLE_BASE_HOME/network/admin``.
Note that the :ref:`easyconnect` can set many common configuration options
without needing ``tnsnames.ora`` or ``sqlnet.ora`` files.
The section :ref:`Network Configuration <hanetwork>` has additional information
about Oracle Net configuration.
.. _envset:
Oracle Environment Variables for python-oracledb Thick Mode
@ -491,9 +474,8 @@ first connection is established. System environment variables like
.. note::
The variables listed below are only supported in the python-oracledb Thick
mode, with the exception of ``TNS_ADMIN`` and ``ORA_SDTZ`` which are also
supported in the python-oracledb Thin mode.
These variables, with the exception of ``TNS_ADMIN``, are only supported in
the python-oracledb Thick mode. See :ref:`enablingthick`.
.. list-table-with-summary:: Common Oracle environment variables
:header-rows: 1
@ -507,7 +489,7 @@ first connection is established. System environment variables like
* - LD_LIBRARY_PATH
- The library search path for platforms like Linux should include the
Oracle libraries, for example ``$ORACLE_HOME/lib`` or
``/opt/instantclient_19_18``. This variable is not needed if the
``/opt/instantclient_19_3``. This variable is not needed if the
libraries are located by an alternative method, such as with
``ldconfig``. On other UNIX platforms, you may need to set an OS
specific equivalent such as ``LIBPATH`` or ``SHLIB_PATH``.
@ -559,8 +541,8 @@ set ``ORA_TZFILE`` to the file name with a directory prefix. For example:
``export ORA_TZFILE=/opt/oracle/myconfig/timezone_31.dat``.
The Oracle Database documentation contains more information about time zone
files, see `Choosing a Time Zone File <https://www.oracle.com/pls/topic/
lookup?ctx=dblatest&id=GUID-805AB986-DE12-4FEA-AF56-5AABCD2132DF>`__.
files, see `Choosing a Time Zone File
<https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-805AB986-DE12-4FEA-AF56-5AABCD2132DF>`__.
.. _otherinit:
@ -569,19 +551,27 @@ Other python-oracledb Thick Mode Initialization
The :meth:`oracledb.init_oracle_client()` function allows ``driver_name`` and
``error_url`` parameters to be set. These are useful for applications whose
end-users are not aware that python-oracledb is being used. An example of
setting the parameters is:
end-users are not aware that python-oracledb is being used. An example of setting
the parameters is:
.. code-block:: python
oracledb.init_oracle_client(driver_name="My Great App : 3.1.4",
error_url="https://example.com/MyInstallInstructions.html")
import oracledb
import sys
try:
oracledb.init_oracle_client(driver_name="My Great App : 3.1.4",
error_url="https://example.com/MyInstallInstructions.html")
except Exception as err:
print("Whoops!")
print(err)
sys.exit(1)
The convention for ``driver_name`` is to separate the product name from the
product version by a colon and single blank characters. The value will be
shown in Oracle Database views like ``V$SESSION_CONNECT_INFO``. If this
parameter is not specified, then a value like ``python-oracledb thk : 1.2.0``
is shown, see :ref:`vsessconinfo`.
parameter is not specified, then a value like "python-oracledb thk : 1.0.0" is
shown, see :ref:`vsessconinfo`.
The ``error_url`` string will be shown in the exception raised if
``init_oracle_client()`` cannot load the Oracle Client libraries. This allows
@ -614,4 +604,4 @@ If you have been using python-oracledb in Thick mode, you can use Thin mode by:
You can validate the python-oracledb mode by querying the ``CLIENT_DRIVER``
column of ``V$SESSION_CONNECT_INFO`` and verifying if the value of the column
begins with the text ``python-oracledb thn``. See :ref:`vsessconinfo`.
begins with ``python-oracledb thn``. See :ref:`vsessconinfo`.

View File

@ -29,23 +29,30 @@ Specification.
Quick Start python-oracledb Installation
========================================
This section contains the steps needed to install python-oracledb.
This section contains the steps that you need to perform to install python-oracledb
quickly.
1. Install `Python 3 <https://www.python.org/downloads>`__ if it is not already
available. Use any version from Python 3.7 through 3.12.
1. Install `Python 3 <https://www.python.org/downloads>`__, if it is not already
available. The version of Python to be used depends on the operating system (OS):
2. Install python-oracledb from `PyPI <https://pypi.org/project/oracledb/>`__,
for example:
- On Windows, use Python 3.7 to 3.11
- On macOS, use Python 3.7 to 3.11
- On Linux, use Python 3.6 to 3.11
By default, python-oracledb connects directly to Oracle Database. This lets
it be used when Oracle Client libraries are not available (such Apple M1 or
Alpine Linux), or where the client libraries are not easily installable (such
as some cloud environments). Note not all environments are tested.
2. Install python-oracledb from `PyPI <https://pypi.org/project/oracledb/>`__:
.. code-block:: shell
python -m pip install oracledb --upgrade
On some platforms the binary may be called ``python3`` instead of ``python``.
If a python-oracledb binary package is not available for your platform, the
source package will be downloaded. This will be compiled and the resulting
binary installed.
If a binary package is not available for your platform, the source package
will be downloaded instead. This will be compiled and the resulting binary
installed.
The ``--user`` option may be useful if you do not have permission to write to
system directories:
@ -54,27 +61,21 @@ This section contains the steps needed to install python-oracledb.
python -m pip install oracledb --upgrade --user
If you are behind a proxy, use the ``--proxy`` option. For example:
.. code-block:: shell
python -m pip install oracledb --upgrade --proxy=http://proxy.example.com:80
By default, python-oracledb connects directly to Oracle Database. This lets
it be used immediately without needing any additional installation of Oracle
Client libraries.
If you are behind a proxy, add a proxy server to the command, for example add
``--proxy=http://proxy.example.com:80``
3. Create a file ``test.py`` such as:
.. code-block:: python
import getpass
# test.py
import oracledb
import os
un = 'scott'
cs = 'localhost/orclpdb'
pw = getpass.getpass(f'Enter password for {un}@{cs}: ')
un = os.environ.get('PYTHON_USERNAME')
pw = os.environ.get('PYTHON_PASSWORD')
cs = os.environ.get('PYTHON_CONNECTSTRING')
with oracledb.connect(user=un, password=pw, dsn=cs) as connection:
with connection.cursor() as cursor:
@ -82,43 +83,33 @@ This section contains the steps needed to install python-oracledb.
for r in cursor.execute(sql):
print(r)
4. Edit ``test.py`` and set the ``un`` and ``cs`` variables to your own
database username and the database connection string, respectively.
4. In your integrated development environment (IDE) or terminal window, set
the three environment variables used by the test program.
A simple :ref:`connection <connhandling>` to the database requires an Oracle
Database `user name and password
<https://www.youtube.com/watch?v=WDJacg0NuLo>`_ and a database
:ref:`connection string <connstr>`. For python-oracledb, a common
connection string format is ``hostname:port/servicename``, using the host
name where the database is running, the Oracle Database service name of the
database instance, and the port that the database is using. If the default
port 1521 is being used, then this component of the connection string is
often omitted.
The database can be on-premises or in the Cloud. The python-oracledb driver
does not include a database.
A simple :ref:`connection <connhandling>` to the database requires an Oracle
Database `user name and password
<https://www.youtube.com/watch?v=WDJacg0NuLo>`_ and a database
:ref:`connection string <connstr>`. Set the environment variables to your
values. For python-oracledb, the connection string is commonly of the format
``hostname/servicename``, using the host name where the database is running
and the Oracle Database service name of the database instance. The database
can be on-premises or in the Cloud. It should be version 12.1 or later. The
python-oracledb driver does not include a database.
5. Run the program as shown below:
.. code-block:: shell
.. code-block:: shell
python test.py
Enter the database password when prompted and the queried date will be shown,
for example:
.. code-block:: shell
Enter password for cj@localhost/orclpdb: xxxxxxxxxx
(datetime.datetime(2023, 9, 21, 8, 24, 4),)
If you run into installation trouble, refer to detailed instructions below, or
see :ref:`troubleshooting`.
The date will be shown.
You can learn more about python-oracledb from the `python-oracledb
documentation <https://python-oracledb.readthedocs.io/en/latest/index.html>`__
and `samples <https://github.com/oracle/python-oracledb/tree/main/samples>`__.
If you run into installation trouble, see `Troubleshooting`_.
Supported Oracle Database Versions
==================================
@ -126,15 +117,14 @@ When python-oracledb is used in the default Thin mode, it connects directly to
the Oracle Database and does not require Oracle Client libraries. Connections
in this mode can be made to Oracle Database 12.1 or later.
To connect to older Oracle Database releases you must have Oracle Client
libraries installed, and enable python-oracledb's :ref:`Thick mode
<enablingthick>`. Connections in this mode can be made to Oracle Database 9.2,
or later, depending on the Oracle Client library version.
To use the :ref:`Thick mode features <featuresummary>` of python-oracledb,
additional Oracle Client libraries must be installed, as detailed in the
subsequent sections. Connections in this mode can be made to Oracle
Database 9.2, or later, depending on the Oracle Client library version.
In python-oracledb Thick mode, Oracle Database's standard client-server network
interoperability allows connections between different versions of Oracle Client
libraries and Oracle Database. For current or previously certified
configurations, see Oracle Support's `Doc ID 207303.1
Oracle's standard client-server network interoperability allows connections
between different versions of Oracle Client libraries and Oracle Database. For
currently certified configurations, see Oracle Support's `Doc ID 207303.1
<https://support.oracle.com/epmos/faces/DocumentDisplay?id=207303.1>`__. In
summary:
@ -155,14 +145,16 @@ then be used to adjust the application behavior accordingly. Any attempt to
use Oracle features that are not supported by a particular mode or client
library/database combination will result in runtime errors.
.. _instreq:
Installation Requirements
=========================
==========================
To use python-oracledb, you need:
- Python 3.7, 3.8, 3.9, 3.10, 3.11 or 3.12
- Python 3.6, 3.7, 3.8, 3.9, 3.10 or 3.11 depending on the operating system:
- Windows: Use Python 3.7 to 3.11
- macOS: Use Python 3.7 to 3.11
- Linux: Use Python 3.6 to 3.11
- The Python cryptography package. This package is automatically installed as a
dependency of python-oracledb. It is strongly recommended that you keep the
@ -196,7 +188,7 @@ repository `PyPI <https://pypi.org/project/oracledb/>`__:
.. code-block:: shell
python -m pip install oracledb --upgrade
python -m pip install oracledb
This will download and install a pre-compiled binary from `PyPI
<https://pypi.org/project/oracledb/>`__ if one is available for your
@ -210,7 +202,7 @@ install with:
.. code-block:: shell
python3 -m pip install oracledb --upgrade --user
python3 -m pip install oracledb --user
The ``--user`` option is useful when you do not have permission to write to
system directories.
@ -218,11 +210,8 @@ system directories.
Other versions of Python can be used on Oracle Linux, see `Python for Oracle
Linux <https://yum.oracle.com/oracle-linux-python.html>`__.
If you are behind a proxy, use the ``--proxy`` option. For example:
.. code-block:: shell
python -m pip install oracledb --upgrade --proxy=http://proxy.example.com:80
If you are behind a proxy, add a proxy server to the command, for example add
``--proxy=http://proxy.example.com:80``
Optionally Install Oracle Client
@ -240,8 +229,8 @@ Oracle Client libraries installed. Oracle Client versions 21, 19, 18, 12 and
or "Basic Light" package for your operating system architecture.
- Alternatively, use the client libraries already available in a locally
installed database such as the free `Oracle Database 23c Free
<https://www.oracle.com/database/free/>`__ release.
installed database such as the free `Oracle Database Express Edition ("XE")
<https://www.oracle.com/database/technologies/appdev/xe.html>`__ release.
To use python-oracledb in Thick mode you must call
:meth:`oracledb.init_oracle_client()` in your application, see
@ -270,12 +259,8 @@ To use python-oracledb Thick mode with Oracle Instant Client zip files:
- `x86 32-bit <https://www.oracle.com/database/technologies/instant-client/linux-x86-32-downloads.html>`__
- `ARM (aarch64) 64-bit <https://www.oracle.com/database/technologies/instant-client/linux-arm-aarch64-downloads.html>`__
Oracle Database 19c is a Long Term Support Release whereas Oracle Database
21c is an Innovation Release. It is recommended to keep up to date with the
latest Oracle Instant Client release updates of your desired major version.
Oracle Instant Client 19c will connect to Oracle Database 11.2 or later.
Oracle Instant Client 21c will connect to Oracle Database 12.1 or later.
The latest version is recommended. Oracle Instant Client 21 will connect to
Oracle Database 12.1 or later.
2. Unzip the package into a single directory that is accessible to your
application. For example:
@ -314,10 +299,6 @@ To use python-oracledb Thick mode with Oracle Instant Client zip files:
export LD_LIBRARY_PATH=/opt/oracle/instantclient_21_6:$LD_LIBRARY_PATH
Make sure this is set in each shell that invokes Python. Web servers and
other daemons commonly reset environment variables so using ``ldconfig`` is
generally preferred instead.
5. If you use optional Oracle configuration files such as ``tnsnames.ora``,
``sqlnet.ora``, or ``oraaccess.xml`` with Instant Client, then put the files
in an accessible directory, for example in
@ -359,12 +340,8 @@ To use python-oracledb with Oracle Instant Client RPMs:
- `Instant Client RPMs for Oracle Linux ARM (aarch64) 8 <https://yum.oracle.com/repo/OracleLinux/OL8/oracle/instantclient/aarch64/index.html>`__
- `Instant Client RPMs for Oracle Linux ARM (aarch64) 7 <https://yum.oracle.com/repo/OracleLinux/OL7/oracle/instantclient/aarch64/index.html>`__
Oracle Database 19c is a Long Term Support Release whereas Oracle Database
21c is an Innovation Release. It is recommended to keep up to date with the
latest Oracle Instant Client release updates of your desired major version.
Oracle Instant Client 19c will connect to Oracle Database 11.2 or later.
Oracle Instant Client 21c will connect to Oracle Database 12.1 or later.
The latest version is recommended. Oracle Instant Client 21 will connect to
Oracle Database 12.1 or later.
2. Install the downloaded RPM with sudo or as the root user. For example:
@ -396,9 +373,6 @@ To use python-oracledb with Oracle Instant Client RPMs:
export LD_LIBRARY_PATH=/usr/lib/oracle/18.5/client64/lib:$LD_LIBRARY_PATH
Web servers and other daemons commonly reset environment variables so using
``ldconfig`` is generally preferred instead.
4. If you use optional Oracle configuration files such as ``tnsnames.ora``,
``sqlnet.ora`` or ``oraaccess.xml`` with Instant Client, then put the files
in an accessible directory, for example in
@ -466,13 +440,14 @@ Use Python's `pip <https://pip.pypa.io/en/latest/installation/>`__ package
to install python-oracledb from Python's package repository `PyPI
<https://pypi.org/project/oracledb/>`__::
python -m pip install oracledb --upgrade
python -m pip install oracledb
If you are behind a proxy, use the ``--proxy`` option. For example:
If you are behind a proxy, add a proxy server to the command, for example add
``--proxy=http://proxy.example.com:80``
.. code-block:: shell
python -m pip install oracledb --upgrade --proxy=http://proxy.example.com:80
python -m pip install oracledb --proxy=http://proxy.example.com:80 --upgrade
This will download and install a pre-compiled binary `if one is available
<https://pypi.org/project/oracledb/>`__ for your architecture. If a
@ -659,27 +634,27 @@ to install python-oracledb from Python's package repository `PyPI
.. code-block:: shell
python -m pip install oracledb --upgrade
python -m pip install oracledb
The ``--user`` option may be useful if you do not have permission to write to
system directories:
.. code-block:: shell
python -m pip install oracledb --upgrade --user
If you are behind a proxy, use the ``--proxy`` option. For example:
.. code-block:: shell
python -m pip install oracledb --upgrade --proxy=http://proxy.example.com:80
python -m pip install oracledb --user
To install into the system Python, you may need to use ``/usr/bin/python3``
instead of ``python``:
.. code-block:: shell
/usr/bin/python3 -m pip install oracledb --upgrade --user
/usr/bin/python3 -m pip install oracledb --user
If you are behind a proxy, add a proxy server to the command, for example add
``--proxy=http://proxy.example.com:80``
The source will be downloaded, compiled, and the resulting binary
installed.
Optionally Install Oracle Client
--------------------------------
@ -812,8 +787,6 @@ code:
- C Compiler: A C99 compiler is needed.
.. _installgh:
Install Using GitHub
--------------------
@ -824,11 +797,6 @@ In order to install using the source on GitHub, use the following commands::
python setup.py build
python setup.py install
If you do not have access to system directories, the ``--user`` option can be
used to install into a local directory::
python setup.py install --user
Note that if you download a source zip file directly from GitHub then you will
also need to download an `ODPI-C <https://github.com/oracle/odpi>`__ source zip
file and put the extracted contents inside the "odpi" subdirectory, for example
@ -842,6 +810,12 @@ can be installed with::
python setup.py build
python setup.py install
If you do not have access to system directories, the ``--user`` option can be
used to install into a local directory::
python setup.py install --user
Install Using Source from PyPI
------------------------------
@ -856,3 +830,131 @@ If you do not have access to system directories, the ``--user`` option can be
used to install into a local directory::
python setup.py install --user
.. _troubleshooting:
Troubleshooting
===============
Installation Troubleshooting
----------------------------
If installation fails:
- An error such as ``not a supported wheel on this platform.`` indicates that
you may be using an older `pip` version. Upgrade it with the following
command:
.. code-block:: shell
python -m pip install pip --upgrade --user
- Use option ``-v`` with pip. Review your output and logs. Try to install
using a different method. **Google anything that looks like an error.**
Try some potential solutions.
- If there was a network connection error, check if you need to set the
environment variables ``http_proxy`` and/or ``https_proxy`` or
try ``python -m pip install --proxy=http://proxy.example.com:80 oracledb
--upgrade``.
- If the upgrade did not give any errors but the old version is still
installed, try ``python -m pip install oracledb --upgrade
--force-reinstall``.
- If you do not have access to modify your system version of
Python, then use ``python -m pip install oracledb --upgrade --user``
or venv.
- If you get the error ``No module named pip``, it means that the pip module
that is built into Python may sometimes be removed by the OS. Use the venv
module (built into Python 3.x) or virtualenv module instead.
- If you get the error ``fatal error: dpi.h: No such file or directory``
when building from source code, then ensure that your source installation has
a subdirectory called "odpi" containing files. If this is missing, review the
section on `Install Using GitHub`_.
.. _runtimetroubleshooting:
Runtime Error Troubleshooting
-----------------------------
If using python-oracledb fails:
- If you have multiple versions of Python installed, ensure that you are
using the correct python and pip (or python3 and pip3) executables.
- If you get the error ``DPI-1047: Oracle Client library cannot be
loaded``:
- Review the :ref:`features available in python-oracledb's default Thin mode
<featuresummary>`. If Thin mode suits your requirements, then remove calls
in your application to :meth:`oracledb.init_oracle_client()` since this
loads the Oracle Client library to enable Thick mode.
- On Windows and macOS, pass the ``lib_dir`` library directory parameter
in your :meth:`oracledb.init_oracle_client()` call. The parameter
should be the location of your Oracle Client libraries. Do not pass
this parameter on Linux.
- Check that the Python process has permission to open the Oracle Client
libraries. OS restrictions may prevent the opening of libraries installed
in unsafe paths, such as from a user directory. On Linux you may need to
install the Oracle Client libraries under a directory like ``/opt`` or
``/usr/local``.
- Check if Python and your Oracle Client libraries are both 64-bit or
both 32-bit. The ``DPI-1047`` message will tell you whether the 64-bit
or 32-bit Oracle Client is needed for your Python.
- Set the environment variable ``DPI_DEBUG_LEVEL`` to 64 and restart
python-oracledb. The trace messages will show how and where
python-oracledb is looking for the Oracle Client libraries.
At a Windows command prompt, this could be done with::
set DPI_DEBUG_LEVEL=64
On Linux and macOS, you might use::
export DPI_DEBUG_LEVEL=64
- On Windows, if you have a full database installation, ensure that this
database is the `currently configured database
<https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-33D575DD-47FF-42B1-A82F-049D3F2A8791>`__.
- On Windows, if you are not using passing a library directory parameter
to :meth:`oracledb.init_oracle_client()`, then restart your command
prompt and use ``set PATH`` to check if the environment variable has the
correct Oracle Client listed before any other Oracle directories.
- On Windows, use the ``DIR`` command to verify that ``OCI.DLL`` exists in
the directory passed to :meth:`oracledb.init_oracle_client()` or set in
``PATH``.
- On Windows, check that the correct `Windows Redistributables
<https://oracle.github.io/odpi/doc/installation.html#windows>`__ have
been installed.
- On Linux, check if the ``LD_LIBRARY_PATH`` environment variable contains
the Oracle Client library directory. Some environments such as web servers
reset environment variables. If you are using Oracle Instant Client, a
preferred alternative to ``LD_LIBRARY_PATH`` is to ensure that a file in
the ``/etc/ld.so.conf.d`` directory contains the path to the Instant Client
directory, and then run ``ldconfig``.
- If you get the error ``DPY-3010: connections to this database server
version are not supported by python-oracledb in thin mode`` when
connecting to Oracle Database 11.2, then you need to enable Thick mode by
installing Oracle Client libraries and calling
:meth:`oracledb.init_oracle_client()` in your code. Alternatively,
upgrade your database.
- If you get the error ``DPI-1072: the Oracle Client library version is
unsupported``, then review the installation requirements. The Thick
mode of python-oracledb needs Oracle Client libraries 11.2 or later.
Note that version 19 is not supported on Windows 7. Similar steps shown
above for ``DPI-1047`` may help. You may be able to use Thin mode which
can be done by removing calls :meth:`oracledb.init_oracle_client()` from
your code.

View File

@ -8,7 +8,6 @@ The python-oracledb driver is a Python extension module that enables access to
Oracle Database. It has comprehensive functionality supporting the `Python
Database API v2.0 Specification <https://www.python.org/dev/peps/pep-0249/>`__
with a considerable number of additions and a couple of exclusions.
Synchronous and :ref:`concurrent <asyncio>` coding styles are supported.
The python-oracledb driver is the renamed, major version successor to
`cx_Oracle 8.3 <https://oracle.github.io/python-cx_Oracle/>`__. For upgrade

View File

@ -15,34 +15,13 @@ For more information about using JSON in Oracle Database see the `Database JSON
Developer's Guide
<https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=ADJSN>`__.
**Oracle Database 21c JSON Data Type**
Oracle Database 21c introduced a dedicated JSON data type with a new `binary
storage format <https://blogs.oracle.com/jsondb/osonformat>`__ that improves
performance and functionality compared with earlier releases. To take
advantage of the dedicated JSON type with Oracle Database 21c, or later, you
can use python-oracledb in Thin or Thick modes. With Thick mode the Oracle
Client libraries should be version 21, or later.
To create a table with a column called ``JSON_DATA`` for JSON data you might
use:
.. code-block:: sql
create table CustomersAsJson (
id integer not null primary key,
json_data json
);
**Oracle Database 12c JSON Data Type**
In Oracle Database 12c, or later, JSON in relational tables can be stored as
BLOB, CLOB or VARCHAR2 data. All of these types can be used with
python-oracledb in Thin or Thick modes. BLOB is preferred to avoid character
set conversion overheads.
In Oracle Database 12c, JSON in relational tables is stored as BLOB, CLOB or
VARCHAR2 data. All of these types can be used with python-oracledb in Thin or
Thick modes.
The syntax to create a table with a JSON column using BLOB storage is like:
The older syntax to create a table with a JSON column is like:
.. code-block:: sql
@ -54,15 +33,35 @@ The syntax to create a table with a JSON column using BLOB storage is like:
The check constraint with the clause ``IS JSON`` ensures only JSON data is
stored in that column.
This older syntax can still be used in Oracle Database 21c (and later);
however, the recommendation is to move to the new JSON type.
The older syntax can still be used in Oracle Database 21c; however, the
recommendation is to move to the new JSON type. With the old syntax, the
storage can be BLOB, CLOB, or VARCHAR2. Of these, BLOB is preferred to avoid
character set conversion overheads.
Using the Oracle Database 21c JSON Type in python-oracledb
==========================================================
**Oracle Database 21c JSON Data Type**
Using python-oracledb Thin mode with Oracle Database 21c, or using Thick mode
with Oracle Database 21c and Oracle Client 21c (or later), you can insert by
binding as shown below:
Oracle Database 21c introduced a dedicated JSON data type with a new `binary
storage format <https://blogs.oracle.com/jsondb/osonformat>`__ that improves
performance and functionality. To fully take advantage of the dedicated JSON
type, use python-oracledb in Thick mode with Oracle Client libraries version
21, or later.
In Oracle Database 21, to create a table with a column called ``JSON_DATA`` for
JSON data you might use:
.. code-block:: sql
create table CustomersAsJson (
id integer not null primary key,
json_data json
);
Using the Oracle Database 21c JSON Type in python-oracledb Thick Mode
=====================================================================
Using python-oracledb Thick mode with Oracle Database 21c and Oracle Client 21c
(or later), you can insert by binding as shown below:
.. code-block:: python
@ -75,8 +74,6 @@ binding as shown below:
cursor.setinputsizes(None, oracledb.DB_TYPE_JSON)
cursor.execute(insert_sql, [1, data])
.. _json21fetch:
To fetch a JSON column, use:
.. code-block:: python
@ -85,49 +82,84 @@ To fetch a JSON column, use:
print(row)
See `json_direct.py
<https://github.com/oracle/python-oracledb/tree/main/samples/json_direct.py>`__
<https://github.com/oracle/python-oracledb/tree/main/samplesjson_direct.py>`__
for a runnable example. The example also shows how to use this type when
python-oracledb Thick mode uses older Oracle Client libraries.
Using the Oracle Database 21c JSON Type and python-oracledb Thin Mode
=====================================================================
Using python-oracledb Thin mode with Oracle Database 21c, you can insert into a
JSON column as shown below:
.. code:: python
data = [
(1, dict(name="Rod", dept="Sales", location="Germany")),
(2, dict(name="George", dept="Marketing", location="Bangalore")),
(3, dict(name="Sam", dept="Sales", location="Mumbai")),
(4, dict(name="Jill", dept="Marketing", location="Germany"))
]
insert_sql = "insert into CustomersAsJson values (:1, :2)"
# Insert the data as a JSON string
cursor.executemany(insert_sql, [(i, json.dumps(j)) for i, j in data])
For python-oracledb Thin mode, a type handler is required to fetch the Oracle
21c JSON datatype. If a type handler is used in the python-oracledb Thick
mode, then the behavior is same in both the python-oracledb modes. The
following example shows a type handler:
.. code-block:: python
def my_type_handler(cursor, name, default_type, size, precision, scale):
if default_type == oracledb.DB_TYPE_JSON:
return cursor.var(str, arraysize=cursor.arraysize, outconverter=json.loads)
cursor.outputtypehandler = my_type_handler
for row in cursor.execute("select * from CustomersAsJson"):
print(row)
With a type handler, the python-oracledb Thin mode is equivalent
to using the python-oracledb Thick mode with Oracle Client 21c. The
python-oracledb Thin mode returns timestamps in a string representation.
Without a type handler, the python-oracledb Thin mode gives an error that
``DB_TYPE_JSON`` is not supported.
A type handler is not needed when fetching from the Oracle Database 19c JSON
type, since this is represented as VARCHAR2 or LOB.
See `json_type.py
<https://github.com/oracle/python-oracledb/tree/main/samplesjson_type.py>`__
for a runnable example.
Using the Oracle 12c JSON type in python-oracledb
=================================================
When using Oracle Database 12c or later with JSON using BLOB storage, you can
insert JSON strings like:
When using Oracle Database 12c or later with JSON using BLOB storage to insert
JSON strings, use:
.. code-block:: python
import json
data = dict(name="Rod", dept="Sales", location="Germany")
inssql = "insert into CustomersAsBlob values (:1, :2)"
cursor.execute(inssql, [1, json.dumps(data)])
You can fetch VARCHAR2 and LOB columns that contain JSON data in the same way
that :ref:`JSON type columns <json21fetch>` are fetched when using Oracle
Database 21c or later. If you are using python-oracledb Thick mode, you must
use Oracle Client 19c (or later). For example:
To fetch JSON strings, use:
.. code-block:: python
for row in cursor.execute("select * from CustomersAsBlob"):
print(row)
import json
.. versionchanged:: 2.0
Previously, the ``oracledb.__future__.old_json_col_as_obj`` attribute
needed to be set to *True* to fetch VARCHAR2 and LOB columns that
contained JSON data. Also, you could fetch JSON data without setting this
attribute with a call to ``json.loads()`` on the returned data. With this
change, the ``oracledb.__future__.old_json_col_as_obj`` attribute is
desupported. VARCHAR2 and LOB columns containing JSON data can now be
fetched directly without setting the
``oracledb.__future__.old_json_col_as_obj`` attribute or without needing
to call ``json.loads()`` on the value.
sql = "SELECT c.json_data FROM CustomersAsBlob c"
for j, in cursor.execute(sql):
print(json.loads(j.read()))
See `json_blob.py
<https://github.com/oracle/python-oracledb/tree/main/samples/json_blob.py>`__
<https://github.com/oracle/python-oracledb/tree/main/samplesjson_blob.py>`__
for a runnable example.
IN Bind Type Mapping
@ -213,18 +245,17 @@ using SQL is:
.. code-block:: python
cursor.execute("""
insert into mytab (
myjsoncol
) values (
json_object(key 'mydocument' value json_scalar(to_clob(:b)) returning json)
)""",
['A short CLOB'])
insert into mytab (myjsoncol) values
(json_object(key 'mydocument' value json_scalar(to_clob(:b))
returning json))""",
['A short CLOB'])
When `mytab` is queried in python-oracledb, the CLOB data will be returned as a
Python string, as shown by the following table. Output might be like::
{mydocument: 'A short CLOB'}
Query and OUT Bind Type Mapping
===============================
@ -298,16 +329,10 @@ some JSON data. To look for JSON entries that have a ``location`` field:
.. code-block:: python
import json
for blob, in cursor.execute("""
select
json_data
from
customers
where
json_exists(json_data,
'$.location')"""):
select json_data
from customers
where json_exists(json_data, '$.location')"""):
data = json.loads(blob.read())
print(data)
@ -326,6 +351,7 @@ For more information, see `SQL/JSON Path Expressions
id=GUID-2DC05D71-3D62-4A14-855F-76E054032494>`__
in the Oracle JSON Developer's Guide.
Accessing Relational Data as JSON
=================================
@ -336,15 +362,10 @@ function is a great way to convert relational table data to JSON:
.. code-block:: python
cursor.execute("""
select
json_object('deptId' is d.department_id,
'name' is d.department_name) department
from
departments d
where
department_id < :did
order by
d.department_id""",
select json_object('deptId' is d.department_id, 'name' is d.department_name) department
from departments d
where department_id < :did
order by d.department_id""",
[50]);
for row in cursor:
print(row)
@ -355,30 +376,3 @@ This produces::
('{"deptId":20,"name":"Marketing"}',)
('{"deptId":30,"name":"Purchasing"}',)
('{"deptId":40,"name":"Human Resources"}',)
To select a result set from a relational query as a single object you can use
`JSON_ARRAYAGG
<https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-6D56077D-78DE-4CC0-9498-225DDC42E054>`__,
for example:
.. code-block:: python
oracledb.defaults.fetch_lobs = False
cursor.execute("""
select
json_arrayagg(
json_object('deptid' is d.department_id,
'name' is d.department_name) returning clob)
from
departments d
where
department_id < :did""",
[50]);
j, = cursor.fetchone()
print(j)
This produces::
[{"deptid":10,"name":"Administration"},{"deptid":20,"name":"Marketing"},{"deptid":30,"name":"Purchasing"},{"deptid":40,"name":"Human Resources"}]

View File

@ -107,12 +107,12 @@ handler:
.. code-block:: python
def output_type_handler(cursor, metadata):
if metadata.type_code is oracledb.DB_TYPE_CLOB:
def output_type_handler(cursor, name, default_type, size, precision, scale):
if default_type == oracledb.DB_TYPE_CLOB:
return cursor.var(oracledb.DB_TYPE_LONG, arraysize=cursor.arraysize)
if metadata.type_code is oracledb.DB_TYPE_BLOB:
if default_type == oracledb.DB_TYPE_BLOB:
return cursor.var(oracledb.DB_TYPE_LONG_RAW, arraysize=cursor.arraysize)
if metadata.type_code is oracledb.DB_TYPE_NCLOB:
if default_type == oracledb.DB_TYPE_NCLOB:
return cursor.var(oracledb.DB_TYPE_LONG_NVARCHAR, arraysize=cursor.arraysize)
connection.outputtypehandler = output_type_handler

View File

@ -34,8 +34,8 @@ then the following Python code can be used to call it:
cursor.callproc('myproc', [123, out_val])
print(out_val.getvalue()) # will print 246
Calling :meth:`Cursor.callproc()` internally generates an :ref:`anonymous PL/SQL
block <anonplsql>` and executes it. This is equivalent to the application code:
Calling :meth:`Cursor.callproc()` actually generates an anonymous PL/SQL block
as shown below, which is then executed:
.. code-block:: python
@ -113,8 +113,6 @@ The Python code that will call this procedure looks as follows:
See :ref:`bind` for information on binding.
.. _anonplsql:
Anonymous PL/SQL Blocks
-----------------------
@ -136,56 +134,34 @@ Creating Stored Procedures and Packages
---------------------------------------
To create PL/SQL stored procedures and packages, use :meth:`Cursor.execute()`
with a CREATE command. For example:
with a SQL CREATE command.
.. code-block:: python
Creation warning messages can be found from database views like USER_ERRORS.
cursor.execute("""
create or replace procedure myprocedure
(p_in in number, p_out out number) as
begin
p_out := p_in * 2;
end;""")
.. _plsqlwarning:
PL/SQL Compilation Warnings
+++++++++++++++++++++++++++
When creating PL/SQL procedures and functions (or creating types) in
python-oracledb, the statement might succeed without throwing an error, but
there may be additional informational messages. (These messages are sometimes
known in Oracle as "success with info" messages). If your application needs to
show such messages, they must be explicitly looked for using
:attr:`Cursor.warning`. A subsequent query from a table like ``USER_ERRORS``
will show more details. For example:
For example, creating a procedure with an error could be like:
.. code-block:: python
with connection.cursor() as cursor:
cursor.execute("""
create or replace procedure badproc as
create or replace procedure badproc (a in number) as
begin
WRONG WRONG WRONG
end;""")
if cursor.warning and cursor.warning.full_code == "DPY-7000":
print(cursor.warning)
# Get details
cursor.execute("""
select line, position, text
from user_errors
where name = 'BADPROC' and type = 'PROCEDURE'
order by line, position""")
for info in cursor:
cursor.execute("""
select line, position, text
from user_errors
where name = 'BADPROC' and type = 'PROCEDURE'
order by name, type, line, position""")
errors = cursor.fetchall()
if errors:
for info in errors:
print("Error at line {} position {}:\n{}".format(*info))
else:
print("Created successfully")
The output would be::
DPY-7000: creation succeeded with compilation errors
Error at line 3 position 23:
PLS-00103: Encountered the symbol "WRONG" when expecting one of the following:
:= . ( @ % ;

View File

@ -16,27 +16,23 @@ PL/SQL statements are discussed in :ref:`plsqlexecution`. Other chapters
contain information on specific data types and features. See :ref:`batchstmnt`,
:ref:`lobdata`, :ref:`jsondatatype`, and :ref:`xmldatatype`.
Python-oracledb can be used to execute individual statements, one at a time.
Once a statement has finished execution, only then will the next statement
execute. If you try to execute statements concurrently, the statements are
queued and run consecutively in the order they are in the code.
Python-oracledb does not read SQL*Plus ".sql" files. To read SQL files, use a
technique like the one in ``run_sql_script()`` in `samples/sample_env.py
<https://github.com/oracle/python-oracledb/blob/main/samples/sample_env.py>`__.
Python-oracledb can be used to execute individual statements, one at a time. It does
not read SQL*Plus ".sql" files. To read SQL files, use a technique like the one
in ``run_sql_script()`` in `samples/sample_env.py
<https://github.com/oracle/python-oracledb/blob/main/samples/sample_env.py>`__
SQL statements should not contain a trailing semicolon (";") or forward slash
("/"). This will fail:
.. code-block:: python
cursor.execute("select * from MyTable;")
cur.execute("select * from MyTable;")
This is correct:
.. code-block:: python
cursor.execute("select * from MyTable")
cur.execute("select * from MyTable")
SQL Queries
@ -52,107 +48,87 @@ optionally :ref:`overridden <outputtypehandlers>`.
.. IMPORTANT::
Interpolating or concatenating user data with SQL statements, for example
``cursor.execute("SELECT * FROM mytab WHERE mycol = '" + myvar + "'")``, is a security risk
``cur.execute("SELECT * FROM mytab WHERE mycol = '" + myvar + "'")``, is a security risk
and impacts performance. Use :ref:`bind variables <bind>` instead. For
example, ``cursor.execute("SELECT * FROM mytab WHERE mycol = :mybv", mybv=myvar)``.
example, ``cur.execute("SELECT * FROM mytab WHERE mycol = :mybv", mybv=myvar)``.
.. _fetching:
Fetch Methods
-------------
Rows can be fetched in various ways.
After :meth:`Cursor.execute()`, the cursor is returned as a convenience. This
allows code to iterate over rows like:
- After :meth:`Cursor.execute()`, the cursor is returned as a convenience. This
allows code to iterate over rows like:
.. code-block:: python
.. code-block:: python
cur = connection.cursor()
for row in cur.execute("select * from MyTable"):
print(row)
cursor = connection.cursor()
for row in cursor.execute("select * from MyTable"):
print(row)
Rows can also be fetched one at a time using the method
:meth:`Cursor.fetchone()`:
- Rows can also be fetched one at a time using the method
:meth:`Cursor.fetchone()`:
.. code-block:: python
.. code-block:: python
cur = connection.cursor()
cur.execute("select * from MyTable")
while True:
row = cur.fetchone()
if row is None:
break
print(row)
cursor = connection.cursor()
cursor.execute("select * from MyTable")
while True:
row = cursor.fetchone()
if row is None:
break
print(row)
If rows need to be processed in batches, the method :meth:`Cursor.fetchmany()`
can be used. The size of the batch is controlled by the ``numRows`` parameter,
which defaults to the value of :attr:`Cursor.arraysize`.
- If rows need to be processed in batches, the method :meth:`Cursor.fetchmany()`
can be used. The size of the batch is controlled by the ``size`` parameter,
which defaults to the value of :attr:`Cursor.arraysize`.
.. code-block:: python
.. code-block:: python
cur = connection.cursor()
cur.execute("select * from MyTable")
num_rows = 10
while True:
rows = cur.fetchmany(num_rows)
if not rows:
break
for row in rows:
print(row)
cursor = connection.cursor()
cursor.execute("select * from MyTable")
num_rows = 10
while True:
rows = cursor.fetchmany(size=num_rows)
if not rows:
break
for row in rows:
print(row)
If all of the rows need to be fetched and can be contained in memory, the
method :meth:`Cursor.fetchall()` can be used.
Note the ``size`` parameter only affects the number of rows returned to the
application, not to the internal buffer size used for tuning fetch
performance. That internal buffer size is controlled only by changing
:attr:`Cursor.arraysize`, see :ref:`tuningfetch`.
.. code-block:: python
- If all of the rows need to be fetched and can be contained in memory, the
method :meth:`Cursor.fetchall()` can be used.
cur = connection.cursor()
cur.execute("select * from MyTable")
rows = cur.fetchall()
for row in rows:
print(row)
.. code-block:: python
cursor = connection.cursor()
cursor.execute("select * from MyTable")
rows = cursor.fetchall()
for row in rows:
print(row)
The fetch methods return data as tuples. To return results as dictionaries, see
:ref:`rowfactories`.
The fetch methods return data as tuples. To return results as dictionaries, see
:ref:`rowfactories`.
Closing Cursors
---------------
Once cursors are no longer needed, they should be closed in order to reclaim
resources in the database. Note cursors may be used to execute multiple
statements.
A cursor may be used to execute multiple statements. Once it is no longer
needed, it should be closed by calling :meth:`~Cursor.close()` in order to
reclaim resources in the database. It will be closed automatically when the
variable referencing it goes out of scope (and no further references are
retained). One other way to control the lifetime of a cursor is to use a "with"
block, which ensures that a cursor is closed once the block is completed. For
example:
Cursors can be closed in various ways:
.. code-block:: python
- A cursor will be closed automatically when the variable referencing it goes out
of scope (and no further references are retained). A ``with`` block is a
convenient way to ensure this. For example:
.. code-block:: python
with connection.cursor() as cursor:
for row in cursor.execute("select * from MyTable"):
print(row)
This code ensures that once the block is completed, the cursor is closed and
database resources can be reclaimed. In addition, any attempt to use the
variable ``cursor`` outside of the block will fail.
- Cursors can be explicitly closed by calling :meth:`~Cursor.close()`
.. code-block:: python
cursor = connection.cursor()
...
cursor.close()
with connection.cursor() as cursor:
for row in cursor.execute("select * from MyTable"):
print(row)
This code ensures that once the block is completed, the cursor is closed and
resources have been reclaimed by the database. In addition, any attempt to use
the variable ``cursor`` outside of the block will simply fail.
.. _querymetadata:
@ -164,9 +140,9 @@ can be obtained using :attr:`Cursor.description`:
.. code-block:: python
cursor = connection.cursor()
cursor.execute("select * from MyTable")
for column in cursor.description:
cur = connection.cursor()
cur.execute("select * from MyTable")
for column in cur.description:
print(column)
This could result in metadata like::
@ -284,155 +260,132 @@ Python object that is returned by default. Python types can be changed with
scalar value is returned.
.. _changingdata:
Changing Fetched Data
---------------------
Data returned by python-oracledb queries can be changed by using output type
handlers, by using "outconverters", or by using row factories.
.. _outputtypehandlers:
Changing Fetched Data Types with Output Type Handlers
+++++++++++++++++++++++++++++++++++++++++++++++++++++
-----------------------------------------------------
Sometimes the default conversion from an Oracle Database type to a Python type
must be changed in order to prevent data loss or to fit the purposes of the
Python application. In such cases, an output type handler can be specified for
queries. This asks the database to do a conversion from the column type to a
different type before the data is returned from the database to
python-oracledb. If the database does not support such a mapping, an error
will be returned. Output type handlers only affect query output and do not
affect values returned from :meth:`Cursor.callfunc()` or
:meth:`Cursor.callproc()`.
queries. Output type handlers do not affect values returned from
:meth:`Cursor.callfunc()` or :meth:`Cursor.callproc()`.
Output type handlers can be specified on a :attr:`connection
<Connection.outputtypehandler>` or on a :attr:`cursor
<Cursor.outputtypehandler>`. If specified on a cursor, fetch type handling is
only changed on that particular cursor. If specified on a connection, all
Output type handlers can be specified on the :attr:`connection
<Connection.outputtypehandler>` or on the :attr:`cursor
<Cursor.outputtypehandler>`. If specified on the cursor, fetch type handling is
only changed on that particular cursor. If specified on the connection, all
cursors created by that connection will have their fetch type handling changed.
The output type handler is expected to be a function with the following
signature::
handler(cursor, metadata)
handler(cursor, name, defaultType, size, precision, scale)
The metadata parameter is a :ref:`FetchInfo object<fetchinfoobj>`, which is the
same value found in :attr:`Cursor.description`.
The parameters are the same information as the query column metadata found in
:attr:`Cursor.description`. The function is called once for each column that is
going to be fetched. The function is expected to return a
:ref:`variable object <varobj>` (generally by a call to :func:`Cursor.var()`)
or the value ``None``. The value ``None`` indicates that the default type
should be used.
The function is called once for each column that is going to be
fetched. The function is expected to return a :ref:`variable object <varobj>`
(generally by a call to :func:`Cursor.var()`) or the value ``None``. The value
``None`` indicates that the default type should be used.
For example:
.. code-block:: python
def output_type_handler(cursor, metadata):
if metadata.type_code is oracledb.DB_TYPE_NUMBER:
return cursor.var(oracledb.DB_TYPE_VARCHAR, arraysize=cursor.arraysize)
This output type handler is called once for each column in the SELECT query.
For each numeric column, the database will now return a string representation
of each row's value. Using it in a query:
.. code-block:: python
cursor.outputtypehandler = output_type_handler
cursor.execute("select 123 from dual")
r = cursor.fetchone()
print(r)
prints ``('123',)`` showing the number was converted to a string. Without the
type handler, the output would have been ``(123,)``.
When creating variables using :meth:`Cursor.var()` in a handler, the
``arraysize`` parameter should be the same as the :attr:`~Cursor.arraysize` of
the query cursor. In python-oracledb Thick mode, the query (and ``var()``)
arraysize multiplied by the byte size of the particular column must be less
than INT_MAX.
To unset an output type handler, set it to ``None``. For example if you had
previously set a type handler on a cursor, you can remove it with:
.. code-block:: python
cursor.outputtypehandler = None
Other examples of output handlers are shown in :ref:`numberprecision`,
:ref:`directlobs` and :ref:`fetching-raw-data`. Also see samples such as
`samples/type_handlers.py
Examples of output handlers are shown in :ref:`numberprecision`,
:ref:`directlobs` and :ref:`fetching-raw-data`. Also see samples such as `samples/type_handlers.py
<https://github.com/oracle/python-oracledb/blob/main/samples/type_handlers.py>`__
.. _numberprecision:
Fetched Number Precision
------------------------
One reason for using an output type handler is to ensure that numeric precision
is not lost when fetching certain numbers. Oracle Database uses decimal numbers
and these cannot be converted seamlessly to binary number representations like
Python floats. In addition, the range of Oracle numbers exceeds that of
floating point numbers. Python has decimal objects which do not have these
limitations and python-oracledb knows how to perform the conversion between Oracle
numbers and Python decimal values if directed to do so.
The following code sample demonstrates the issue:
.. code-block:: python
cur = connection.cursor()
cur.execute("create table test_float (X number(5, 3))")
cur.execute("insert into test_float values (7.1)")
connection.commit()
cur.execute("select * from test_float")
val, = cur.fetchone()
print(val, "* 3 =", val * 3)
This displays ``7.1 * 3 = 21.299999999999997``
Using Python decimal objects, however, there is no loss of precision:
.. code-block:: python
import decimal
def number_to_decimal(cursor, name, default_type, size, precision, scale):
if default_type == oracledb.DB_TYPE_NUMBER:
return cursor.var(decimal.Decimal, arraysize=cursor.arraysize)
cur = connection.cursor()
cur.outputtypehandler = number_to_decimal
cur.execute("select * from test_float")
val, = cur.fetchone()
print(val, "* 3 =", val * 3)
This displays ``7.1 * 3 = 21.3``
The Python ``decimal.Decimal`` converter gets called with the string
representation of the Oracle number. The output from ``decimal.Decimal`` is
returned in the output tuple.
See `samples/return_numbers_as_decimals.py
<https://github.com/oracle/python-oracledb/blob/main/samples/return_numbers_as_decimals.py>`__
.. _outconverters:
Changing Query Results with Outconverters
+++++++++++++++++++++++++++++++++++++++++
-----------------------------------------
Python-oracledb "outconverters" can be used with :ref:`output type handlers
<outputtypehandlers>` to change returned data.
For example:
For example, to make queries return empty strings instead of NULLs:
.. code-block:: python
def output_type_handler(cursor, metadata):
def out_converter(value):
if value is None:
return ''
return value
def out_converter(d):
if isinstance(d, str):
return f"{d} was a string"
else:
return f"{d} was not a string"
def output_type_handler(cursor, name, default_type, size, precision, scale):
if default_type in (oracledb.DB_TYPE_VARCHAR, oracledb.DB_TYPE_CHAR):
return cursor.var(str, size, arraysize=cur.arraysize,
outconverter=out_converter)
if metadata.type_code is oracledb.DB_TYPE_NUMBER:
return cursor.var(oracledb.DB_TYPE_VARCHAR,
arraysize=cursor.arraysize, outconverter=out_converter)
connection.outputtypehandler = output_type_handler
The output type handler is called once for each column in the SELECT query.
For each numeric column, the database will now return a string representation
of each row's value. The outconverter will then be called in Python for each
of those values. Using it in a query:
.. code-block:: python
cursor.outputtypehandler = output_type_handler
cursor.execute("select 123 as col1, 'abc' as col2 from dual")
for r in cursor.fetchall():
print(r)
prints::
('123 was a string', 'abc')
This shows that the number was first converted to a string by the database, as
requested in the output type handler. The ``out_converter`` function then
appended "was a string" to the data before the value was returned to the
application.
Note outconverters are not called for NULL data values unless the value
specified in the ``convert_nulls`` parameter was *True* when the variable was
created using :meth:`Cursor.var()`.
.. _rowfactories:
Changing Query Results with Rowfactories
++++++++++++++++++++++++++++++++++++++++
----------------------------------------
Python-oracledb "rowfactories" are methods called for each row retrieved from
the database. The :meth:`Cursor.rowfactory` method is called with the tuple
fetched from the database before it is returned to the application. The method
can convert the tuple to a different value.
Python-oracledb "rowfactories" are methods called for each row that is retrieved from
the database. The :meth:`Cursor.rowfactory` method is called with the tuple that
would normally be returned from the database. The method can convert the tuple
to a different value and return it to the application in place of the tuple.
For example, to fetch each row of a query as a dictionary:
.. code-block:: python
cursor.execute("select * from locations where location_id = 1000")
columns = [col[0] for col in cursor.description]
cursor.rowfactory = lambda *args: dict(zip(columns, args))
data = cursor.fetchone()
@ -440,9 +393,7 @@ For example, to fetch each row of a query as a dictionary:
The output is::
{'LOCATION_ID': 1000, 'STREET_ADDRESS': '1297 Via Cola di Rie',
'POSTAL_CODE': '00989', 'CITY': 'Roma', 'STATE_PROVINCE': None,
'COUNTRY_ID': 'IT'}
{'LOCATION_ID': 1000, 'STREET_ADDRESS': '1297 Via Cola di Rie', 'POSTAL_CODE': '00989', 'CITY': 'Roma', 'STATE_PROVINCE': None, 'COUNTRY_ID': 'IT'}
If you join tables where the same column name occurs in both tables with
different meanings or values, then use a column alias in the query. Otherwise,
@ -457,103 +408,6 @@ only one of the similarly named columns will be included in the dictionary:
dogs.color
from cats, dogs
An example showing an :ref:`output type handler <outputtypehandlers>`, an
:ref:`outconverter <outconverters>`, and a row factory is:
.. code-block:: python
def output_type_handler(cursor, metadata):
def out_converter(d):
if type(d) is str:
return f"{d} was a string"
else:
return f"{d} was not a string"
if metadata.type_code is oracledb.DB_TYPE_NUMBER:
return cursor.var(oracledb.DB_TYPE_VARCHAR,
arraysize=cursor.arraysize, outconverter=out_converter)
cursor.outputtypehandler = output_type_handler
cursor.execute("select 123 as col1, 'abc' as col2 from dual")
columns = [col[0] for col in cursor.description]
cursor.rowfactory = lambda *args: dict(zip(columns, args))
for r in cursor.fetchall():
print(r)
The database converts the number to a string before it is returned to
python-oracledb. The outconverter appends "was a string" to this value.
Finally the row factory changes the complete row to a dictionary. The output
is::
{'COL1': '123 was a string', 'COL2': 'abc'}
.. _numberprecision:
Fetched Number Precision
------------------------
Oracle Database uses decimal numbers and these cannot be converted seamlessly
to binary number representations like Python floats. In addition, the range of
Oracle numbers exceeds that of floating point numbers. Python has decimal
objects which do not have these limitations. In python-oracledb you can set
``oracledb.defaults.fetch_decimals`` so that Decimals are returned to the
application, ensuring that numeric precision is not lost when fetching certain
numbers.
The following code sample demonstrates the issue:
.. code-block:: python
cursor.execute("create table test_float (X number(5, 3))")
cursor.execute("insert into test_float values (7.1)")
cursor.execute("select * from test_float")
val, = cursor.fetchone()
print(val, "* 3 =", val * 3)
This displays ``7.1 * 3 = 21.299999999999997``
Using Python decimal objects, however, there is no loss of precision:
.. code-block:: python
oracledb.defaults.fetch_decimals = True
cursor.execute("select * from test_float")
val, = cursor.fetchone()
print(val, "* 3 =", val * 3)
This displays ``7.1 * 3 = 21.3``
See `samples/return_numbers_as_decimals.py
<https://github.com/oracle/python-oracledb/blob/main/samples/return_numbers_as_decimals.py>`__
An equivalent, longer, older coding idiom to :attr:`Defaults.fetch_decimals` is
to use an :ref:`output type handler <outputtypehandlers>` do the conversion.
.. code-block:: python
import decimal
def number_to_decimal(cursor, metadata):
if metadata.type_code is oracledb.DB_TYPE_NUMBER:
return cursor.var(decimal.Decimal, arraysize=cursor.arraysize)
cursor.outputtypehandler = number_to_decimal
cursor.execute("select * from test_float")
val, = cursor.fetchone()
print(val, "* 3 =", val * 3)
This displays ``7.1 * 3 = 21.3``
The Python ``decimal.Decimal`` converter gets called with the string
representation of the Oracle number. The output from ``decimal.Decimal`` is
returned in the output tuple.
.. _scrollablecursors:
Scrollable Cursors
@ -614,8 +468,8 @@ then it can be queried and printed:
.. code-block:: python
cursor.execute("select geometry from mygeometrytab")
for obj, in cursor:
cur.execute("select geometry from mygeometrytab")
for obj, in cur:
dumpobject(obj)
Where ``dumpobject()`` is defined as:
@ -716,8 +570,8 @@ rows using:
ORDER BY last_name
OFFSET :offset ROWS FETCH NEXT :maxnumrows ROWS ONLY"""
cursor = connection.cursor()
for row in cursor.execute(sql, offset=myoffset, maxnumrows=mymaxnumrows):
cur = connection.cursor()
for row in cur.execute(sql, offset=myoffset, maxnumrows=mymaxnumrows):
print(row)
In applications where the SQL query is not known in advance, this method
@ -830,8 +684,9 @@ The following sample demonstrates how to use this feature:
.. code-block:: python
# define output type handler
def return_strings_as_bytes(cursor, metadata):
if metadata.type_code is oracledb.DB_TYPE_VARCHAR:
def return_strings_as_bytes(cursor, name, default_type, size,
precision, scale):
if default_type == oracledb.DB_TYPE_VARCHAR:
return cursor.var(str, arraysize=cursor.arraysize,
bypass_decode=True)
@ -905,10 +760,9 @@ columns:
.. code-block:: python
def output_type_handler(cursor, metadata):
if metadata.type_code is oracledb.DB_TYPE_VARCHAR:
return cursor.var(metadata.type_code, size,
arraysize=cursor.arraysize,
def output_type_handler(cursor, name, default_type, size, precision, scale):
if default_type == oracledb.DB_TYPE_VARCHAR:
return cursor.var(default_type, size, arraysize=cursor.arraysize,
encoding_errors="replace")
cursor.outputtypehandler = output_type_handler
@ -929,8 +783,8 @@ easily be executed with python-oracledb. For example:
.. code-block:: python
cursor = connection.cursor()
cursor.execute("insert into MyTable values (:idbv, :nmbv)", [1, "Fredico"])
cur = connection.cursor()
cur.execute("insert into MyTable values (:idbv, :nmbv)", [1, "Fredico"])
Do not concatenate or interpolate user data into SQL statements. See
:ref:`bind` instead.
@ -953,6 +807,6 @@ SDO_GEOMETRY <spatial>` object:
.. code-block:: python
type_obj = connection.gettype("SDO_GEOMETRY")
cursor = connection.cursor()
cursor.setinputsizes(type_obj)
cursor.execute("insert into sometable values (:1)", [None])
cur = connection.cursor()
cur.setinputsizes(type_obj)
cur.execute("insert into sometable values (:1)", [None])

View File

@ -21,15 +21,11 @@ There are multiple approaches for application tracing and monitoring:
tracing capabilities <https://docs.python.org/3/library/trace.html>`__ can be
used.
- The Java Debug Wire Protocol (JDWP) for debugging PL/SQL can be used. See
:ref:`jdwp`.
- The Java Debug Wire Protocol (JDWP) for debugging PL/SQL can be used. See :ref:`jdwp`.
- Python-oracledb in Thick mode can dump a trace of SQL statements
executed. See :ref:`lowlevelsqltrace`.
- The connection identifiers that appear in the traces and logs can be used
to resolve connectivity errors. See :ref:`connectionid`.
.. _endtoendtracing:
Oracle Database End-to-End Tracing
@ -238,61 +234,9 @@ See `ODPI-C Debugging
<https://oracle.github.io/odpi/doc/user_guide/debugging.html>`__ for
documentation on ``DPI_DEBUG_LEVEL``.
.. _connectionid:
Using Connection Identifiers
----------------------------
A unique connection identifier (``CONNECTION_ID``) is generated for each
connection to the Oracle Database. The connection identifier is shown in some
Oracle Network error messages and logs, which helps in better tracing and
diagnosing of connection failures. For example::
DPY-6005: cannot connect to database (CONNECTION_ID=m0PfUY6hYSmWPcgrHZCQIQ==)
You can define a prefix value which is added to the beginning of the
``CONNECTION_ID``. This prefix aids in identifying the connections from a
specific application.
In python-oracledb Thin mode, you can specify a prefix in the
``connection_id_prefix`` parameter when creating
:meth:`standalone connections <oracledb.connect()>`, or
:meth:`pooled connections <oracledb.create_pool()>`. Also, you can specify
the connection identifier in :meth:`oracledb.ConnectParams()` or
:meth:`oracledb.PoolParams()`. For example:
.. code-block:: python
connection = oracledb.connect(user="hr", password=userpwd,
dsn="localhost/orclpdb",
connection_id_prefix="MYAPP")
If this connection to the database fails, ``MYAPP`` is added as a prefix to the
``CONNECTION_ID`` as shown in the error message below::
DPY-6005: cannot connect to database (CONNECTION_ID=MYAPPm0PfUY6hYSmWPcgrHZCQIQ==).
In python-oracledb Thick mode, you can specify the connection identifier prefix in
a connection string. For example::
mydb = (DESCRIPTION =
(ADDRESS_LIST= (ADDRESS=...) (ADDRESS=...))
(CONNECT_DATA=
(SERVICE_NAME=sales.us.example.com)
(CONNECTION_ID_PREFIX=MYAPP)
)
)
Depending on the Oracle Database version in use, the information that is shown
in logs varies.
See `Troubleshooting Oracle Net Services <https://www.oracle.com/pls/topic/
lookup?ctx=dblatest&id=GUID-3F42D057-C9AC-4747-B48B-5A5FF7672E5D>`_ for more
information on connection identifiers.
.. _vsessconinfo:
Finding the python-oracledb Mode
Finding the Python-oracledb Mode
================================
The boolean attributes :attr:`Connection.thin` and :attr:`ConnectionPool.thin`

View File

@ -1,369 +0,0 @@
.. _troubleshooting:
**********************
Troubleshooting Errors
**********************
Use the troubleshooting information documented in this section to address
errors that may appear while :ref:`installing <instllntroubleshooting>` or
:ref:`using <runtimetroubleshooting>` python-oracledb in Thin or Thick mode.
.. _instllntroubleshooting:
Installation Errors
===================
You may encounter issues while installing python-oracledb. Review the
python-oracledb :ref:`instreq`. To understand the problem better, you can
display more output by using the ``-v`` option with the pip install command.
Review your output and logs.
If you need to modify your system version of Python and do not have the
necessary access to modify it, then use:
.. code-block:: shell
python -m pip install oracledb --upgrade --user
Or you can use the venv module (built into Python 3.x).
This section covers some specific errors or problems that you may run into
during python-oracledb installation, and possible solutions to resolve them.
If the installation problem still persists, then try to install
python-oracledb using a different method. **Google anything that looks like an
error.** Try some potential solutions.
.. _wheelerr:
"Not a supported wheel on this platform" Error
----------------------------------------------
**Cause:** The python-oracledb installation failed with this error because
you may be using an older version of ``pip``.
**Action:** Upgrade your pip version with the following command:
.. code-block:: shell
python -m pip install pip --upgrade --user
.. _networkerr:
Network Connection Error
------------------------
**Cause:** The python-oracledb installation failed because of a network
connection error.
**Action:** To resolve this issue:
- Check if you need to set the environment variables ``http_proxy`` and/or
``https_proxy``.
- Or try the following command:
.. code-block:: shell
python -m pip install --proxy=http://proxy.example.com:80 oracledb --upgrade
.. _upgradeerr:
Upgrade to Latest Version Failed Without Any Errors
---------------------------------------------------
**Cause:** The upgrade to the latest python-oracledb version failed without
any errors because the old version is still installed.
**Action:** To reinstall the latest version, try the following command:
.. code-block:: shell
python -m pip install oracledb --upgrade --force-reinstall
.. _nomodpiperr:
"No module named pip" Error
---------------------------
**Cause:** The python-oracledb installation failed with this error because
the pip module that is built into Python may sometimes be removed by the OS.
**Action:** Instead, use the venv module (built into Python 3.x) or virtualenv
module.
.. _dpiherr:
"fatal error: dpi.h: No such file or directory" Error
-----------------------------------------------------
**Cause:** The python-oracledb installation from source code failed because a
subdirectory called "odpi" may be missing.
**Action:** Check if your source installation has a subdirectory called
"odpi" containing files. If this is missing, review the section on
:ref:`installgh`.
.. _warningmsgs:
Warning Messages
================
Some warnings may appear while using python-oracledb in Thick or Thin mode.
.. _pythwarning:
Deprecated Python Versions 3.6 and 3.7 Warning
----------------------------------------------
**Warning:** ``Python 3.6 is no longer supported by the Python core team.
Therefore, support for it is deprecated in python-oracledb and will be removed
in a future release.`` (A similar warning will also be displayed for Python
version 3.7.)
**Cause:** ``import oracledb`` gives this warning because you are using
deprecated Python versions 3.6 or 3.7 which will be removed in a future
release by the Python core team.
**Action:** You can either:
- Upgrade your Python version to 3.8 or later.
- Or you can temporarily suppress the warning by importing the
`warnings <https://docs.python.org/3/library/warnings.html>`__ module and
adding a call like ``warnings.filterwarnings(action='ignore',
module="oracledb")`` *before* importing ``oracledb``.
.. _runtimetroubleshooting:
Error Messages
==============
While using python-oracledb in Thin or Thick mode, you may encounter
errors. Some common :ref:`DPI <dpierr>` and :ref:`DPY <dpyerr>` errors are
detailed in this section with information about the probable cause of the
error, and the recommended action which may resolve the error.
If you have multiple versions of Python installed, ensure that you are
using the correct python and pip (or python3 and pip3) executables.
.. _dpierr:
DPI Error Messages
------------------
The error messages with prefix ``DPI`` are generated by the
`ODPI-C <https://oracle.github.io/odpi/>`_ code which is used by the
python-oracledb Thick mode.
DPI-1047
++++++++
**Message:** ``DPI-1047: Oracle Client library cannot be loaded``
**Cause:** The connection to Oracle Database failed because the Oracle
Client library could not be loaded.
**Action:** Perform the following steps:
- Review the :ref:`features available in python-oracledb's default Thin mode
<featuresummary>`. If Thin mode suits your requirements, then remove the
calls in your application to :meth:`oracledb.init_oracle_client()` since
this loads the Oracle Client library to :ref:`enable Thick mode
<enablingthick>`.
- On Windows and macOS, pass the ``lib_dir`` library directory parameter
in your :meth:`oracledb.init_oracle_client()` call. The parameter
should be the location of your Oracle Client libraries. Do not pass
this parameter on Linux.
- Check if the Python process has permission to open the Oracle Client
libraries. OS restrictions may prevent the opening of libraries installed
in unsafe paths, such as from a user directory. On Linux you may need to
install the Oracle Client libraries under a directory like ``/opt`` or
``/usr/local``.
- Check if Python and your Oracle Client libraries are both 64-bit or
both 32-bit. The ``DPI-1047`` message will tell you whether the 64-bit
or 32-bit Oracle Client is needed for your Python.
- Set the environment variable ``DPI_DEBUG_LEVEL`` to 64 and restart
python-oracledb. The trace messages will show how and where
python-oracledb is looking for the Oracle Client libraries.
At a Windows command prompt, this could be done with::
set DPI_DEBUG_LEVEL=64
On Linux and macOS, you might use::
export DPI_DEBUG_LEVEL=64
- On Windows:
- If you have a full database installation, ensure that this database is the
`currently configured database <https://docs.oracle.com/pls/topic/lookup?
ctx=db21&id=RIWIN-GUID-33D575DD-47FF-42B1-A82F-049D3F2A8791>`__.
- If you are not passing a library directory parameter to
:meth:`oracledb.init_oracle_client()`, then restart your command prompt
and use ``set PATH`` to check if the environment variable has the correct
Oracle Client listed before any other Oracle directories.
- Use the ``DIR`` command to verify that ``OCI.DLL`` exists in the directory
passed to :meth:`oracledb.init_oracle_client()` or set in ``PATH``.
- Check if the correct `Windows Redistributables <https://oracle.github.io/odpi/
doc/installation.html#windows>`__ have been installed.
- On Linux:
- Check if the ``LD_LIBRARY_PATH`` environment variable contains the Oracle
Client library directory. Some environments such as web servers and
daemons reset environment variables.
- If you are using Oracle Instant Client, a preferred alternative to
``LD_LIBRARY_PATH`` is to ensure that a file in the ``/etc/ld.so.conf.d``
directory contains the path to the Instant Client directory, and then run
``ldconfig``.
DPI-1072
++++++++
**Message:** ``DPI-1072: the Oracle Client library version is unsupported``
**Cause:** The connection to Oracle Database failed because the Oracle Client
library version used is not supported by python-oracledb Thick mode. The Thick
mode needs Oracle Client library 11.2 or later. Note that version 19 is not
supported on Windows 7.
**Action:** Review the :ref:`instreq`. You can either:
- Follow the steps documented in `dpi-1047`_ which may help.
- Or may be use python-oracledb Thin mode which can be done by removing calls
to :meth:`oracledb.init_oracle_client()` from your code.
.. _dpyerr:
DPY Error Messages
------------------
The python-oracledb Thin mode code and python-oracledb Thick mode code
generates error messages with the prefix ``DPY``.
DPY-3010
++++++++
**Message:** ``DPY-3010: connections to this database server version are not
supported by python-oracledb in thin mode``
**Cause:** The connection to Oracle Database with python-oracledb Thin mode
failed because you are using Oracle Database version 11.2 or earlier. Using
python-oracledb Thin mode, you can connect directly to Oracle Database 12.1
or later.
**Action:** You can either:
- :ref:`Enable python-oracledb Thick mode <enablingthick>` since this mode can
connect to Oracle Database 9.2 or later. For Thick mode, you need to install
Oracle Client libraries and call :meth:`oracledb.init_oracle_client()` in
your code.
- Upgrade your Oracle database to python-oracledb Thin mode supported versions
12.1 or later.
DPY-3015
++++++++
**Message:** ``DPY-3015: password verifier type 0x939 is not supported by
python-oracledb in thin mode``
**Cause:** The connection to Oracle Database with python-oracledb Thin mode
failed because your user account was only created with the 10G password
verifier. The python-oracledb Thin mode supports password verifiers 11G and
later.
**Action:** You can either:
- :ref:`Enable Thick mode <enablingthick>` since python-oracledb Thick mode
supports password verifiers 10G and later.
- Or you can:
1. Ensure that the database initialization parameter
``sec_case_sensitive_logon`` is not *FALSE*. To check the value, connect
as SYSDBA in SQL*Plus and run::
show parameter sec_case_sensitive_logon
Note this parameter has been `removed in Oracle Database 21c
<https://docs.oracle.com/en/database/oracle/oracle-database/21/nfcon/
security-solutions.html#GUID-FAF4C7A6-A2CD-4B9B-9A64-3705F693ECF0>`__
so only step 2 is required for this, or subsequent, database versions.
2. Regenerate passwords for users who have old password verifiers. You can
find such users with the query:
.. code-block:: sql
select username from dba_users
where (password_versions = '10G ' or password_versions = '10G HTTP ')
and username <> 'ANONYMOUS';
You can reset passwords for these users with commands like::
alter user x identified by y
.. seealso::
`Finding and Resetting User Passwords That Use the 10G Password
Version <https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=
GUID-D7B09DFE-F55D-449A-8F8A-174D89936304>`__ for more information.
DPY-4011
++++++++
**Message:** ``DPY-4011: the database or network closed the connection``
**Cause:** The connection failed because the Oracle Database that you are
trying to connect to using python-oracledb Thin mode may have Native Network
Encryption (NNE) enabled. NNE is only supported in python-oracledb Thick mode.
**Action:** To verify if NNE is enabled, you can use the following query::
SELECT network_service_banner FROM v$session_connect_info;
If NNE is enabled, then this query prints output that includes the
available encryption service, the crypto-checksumming service, and the
algorithms in use, such as::
NETWORK_SERVICE_BANNER
-------------------------------------------------------------------------------------
TCP/IP NT Protocol Adapter for Linux: Version 19.0.0.0.0 - Production
Encryption service for Linux: Version 19.0.1.0.0 - Production
AES256 Encryption service adapter for Linux: Version 19.0.1.0.0 - Production
Crypto-checksumming service for Linux: Version 19.0.1.0.0 - Production
SHA256 Crypto-checksumming service adapter for Linux: Version 19.0.1.0.0 - Production
If NNE is not enabled, then the query will only print the available encryption
and crypto-checksumming services in the output. For example::
NETWORK_SERVICE_BANNER
-------------------------------------------------------------------------------------
TCP/IP NT Protocol Adapter for Linux: Version 19.0.0.0.0 - Production
Encryption service for Linux: Version 19.0.1.0.0 - Production
If NNE is enabled, you can resolve this error by either:
- Changing the architecture to use Transport Layer Security (TLS) which is
supported in python-oracledb Thin mode instead of using NNE.
- Or :ref:`enabling python-oracledb Thick mode <enablingthick>` since NNE is
supported in Thick mode.
.. seealso::
`Oracle Database Security Guide <https://www.oracle.com/pls/topic/lookup?
ctx=dblatest&id=DBSEG>`__ for more information about Oracle Data Network
Encryption and Integrity, and for information about configuring TLS
network encryption.

View File

@ -14,7 +14,7 @@ Some general tuning tips are:
For multi-user applications, make use of connection pooling. Create the pool
once during application initialization. Do not oversize the pool, see
:ref:`connpooling` . Use a session callback function to set session state, see
:ref:`Session Callbacks for Setting Pooled Connection State <sessioncallback>`.
:ref:`Session CallBacks for Setting Pooled Connection State <sessioncallback>`.
Make use of efficient python-oracledb functions. For example, to insert
multiple rows use :meth:`Cursor.executemany()` instead of
@ -46,26 +46,12 @@ Some general tuning tips are:
* Tune your network. For example, when inserting or retrieving a large number
of rows (or for large data), or when using a slow network, then tune the
Oracle Network Session Data Unit (SDU) and socket buffer sizes, see
`Configuring Session Data Unit
<https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-86D61D6F-AD26-421A-BABA-77949C8A2B04>`__
and `Oracle Net Services: Best Practices for Database Performance and High
Availability
<https://static.rainfocus.com/oracle/oow19/sess/1553616880266001WLIh/PF/
OOW19_Net_CON4641_1569022126580001esUl.pdf>`__.
Oracle Network Session Data Unit (SDU) and socket buffer sizes, see `Oracle
Net Services: Best Practices for Database Performance and High Availability
<https://static.rainfocus.com/oracle/oow19/sess/1553616880266001WLIh/PF/OOW19_Net_CON4641_1569022126580001esUl.pdf>`__.
In python-oracledb Thick mode the SDU size is configured in the
:ref:`optnetfiles`. In python-oracledb Thin mode, the SDU size can be passed
as a connection or pool creation parameter. In both modes it may optionally
be set in the connection :ref:`Easy Connect string <easyconnect>` or
:ref:`connect descriptor <netservice>`.
* Do not commit or rollback unnecessarily. Use :attr:`Connection.autocommit`
on the last of a sequence of DML statements.
* If Python's Global Interpreter Lock (GIL) is limiting
:ref:`concurrent program performance <asyncio>`, then explore using parallel
Python processes.
* Do not commit or rollback unnecessarily. Use :attr:`Connection.autocommit` on
the last of a sequence of DML statements.
.. _tuningfetch:
@ -74,62 +60,74 @@ Tuning Fetch Performance
To tune queries, you can adjust python-oracledb's internal buffer sizes to
improve the speed of fetching rows across the network from the database, and to
optimize memory usage. This can reduce :ref:`round-trips <roundtrips>` which
helps performance and scalability. Tune "array fetching" with
:attr:`Cursor.arraysize` and tune "row prefetching" with
:attr:`Cursor.prefetchrows`. Set these before calling
:meth:`Cursor.execute()`. The value used for prefetching can also be set in an
``oraaccess.xml`` file, see :ref:`optclientfiles`. In python-oracledb Thick
mode, the internal buffers allocated for ``prefetchrows`` and ``arraysize`` are
separate, so increasing both settings will require more Python process memory.
Queries that return LOBs and similar types will never prefetch rows, so the
``prefetchrows`` value is ignored in those cases.
optimize memory usage. Regardless of which :ref:`python-oracledb method
<fetching>` is used to get query results, internally all rows are fetched in
batches from the database and buffered before being returned to the
application. The internal buffer sizes can have a significant performance
impact. The buffer sizes do not affect how or when rows are returned to your
application. They do not affect the minimum or maximum number of rows returned
by a query.
The internal buffer sizes do not affect how or when rows are returned to your
application regardless of which :ref:`python-oracledb method <fetching>` is
used to fetch query results. They do not affect the minimum or maximum number
of rows returned by a query.
For best performance, tune "array fetching" with :attr:`Cursor.arraysize` and
"row prefetching" with :attr:`Cursor.prefetchrows` before calling
:meth:`Cursor.execute()`. Queries that return LOBs and similar types will never
prefetch rows, so the ``prefetchrows`` value is ignored in those cases.
The difference between row prefetching and array fetching is when the internal
buffering occurs. Internally python-oracledb performs separate "execute SQL
statement" and "fetch data" steps. Prefetching allows query results to be
The common query tuning scenario is for SELECT statements that return a large
number of rows over a slow network. Increasing ``arraysize`` can improve
performance by reducing the number of :ref:`round-trips <roundtrips>` to the
database. However increasing this value increases the amount of memory
required. Adjusting ``prefetchrows`` will also affect performance and memory
usage.
Row prefetching and array fetching are both internal buffering techniques to
reduce :ref:`round-trips <roundtrips>` to the database. The difference is when
the buffering occurs. Internally python-oracledb performs separate "execute
SQL statement" and "fetch data" steps. Prefetching allows query results to be
returned to the application when the acknowledgment of successful statement
execution is returned from the database. This means that the subsequent
internal "fetch data" operation does not always need to make a round-trip to
the database because rows are already buffered in python-oracledb or in the
Oracle Client libraries. An overhead of prefetching when using the
python-oracledb Thick mode is the need for additional data copies from Oracle
Client's prefetch buffer when fetching the first batch of rows. This cost may
outweigh the benefits of using prefetching in some cases.
Oracle Client libraries. Reducing round-trips helps performance and
scalability. An overhead of prefetching when using the python-oracledb Thick
mode is the need for additional data copies from Oracle Client's prefetch
buffer.
Choosing values for ``arraysize`` and ``prefetchrows``
------------------------------------------------------
The best :attr:`Cursor.arraysize` and :attr:`Cursor.prefetchrows` values can be
found by experimenting with your application under the expected load of normal
application use. The reduction of round-trips may help performance and overall
system scalability. The documentation in :ref:`round-trips <roundtrips>` shows
how to measure round-trips.
application use. This is because the cost of the extra memory copy from the
prefetch buffer when fetching a large quantity of rows or very "wide" rows may
outweigh the cost of a round-trip for a single python-oracledb user on a fast
network. However under production application load, the reduction of
round-trips may help performance and overall system scalability. The
documentation in :ref:`round-trips <roundtrips>` shows how to measure
round-trips.
Here are some suggestions for tuning:
* To tune queries that return an unknown, large, number of rows, estimate the
number of rows returned and increase the :attr:`Cursor.arraysize` value for
best performance, memory and round-trip usage. The default is 100. For
example:
* To tune queries that return an unknown number of rows, estimate the number of
rows returned and start with an appropriate :attr:`Cursor.arraysize` value.
The default is 100. Then set :attr:`Cursor.prefetchrows` to the ``arraysize``
value. For example:
.. code-block:: python
cur = connection.cursor()
cur.prefetchrows = 1000
cur.arraysize = 1000
for row in cur.execute("SELECT * FROM very_big_table"):
print(row)
In general for this scenario, leave ``prefetchrows`` at its default value.
If you do change it, then set ``arraysize`` as big, or bigger. Do not make
the sizes unnecessarily large.
Adjust the values as needed for performance, memory and round-trip usage. Do
not make the sizes unnecessarily large. For a large quantity of rows or very
"wide" rows on fast networks you may prefer to leave ``prefetchrows`` at its
default value of 2. Keep ``arraysize`` as big, or bigger than,
``prefetchrows``.
* If you are fetching a fixed number of rows, set ``arraysize`` to the number
of expected rows, and set ``prefetchrows`` to one greater than this value.
@ -160,60 +158,11 @@ Here are some suggestions for tuning:
.. code-block:: python
cur = connection.cursor()
cur.arraysize = 1
cur.execute("select * from MyTable where id = 1"):
row = cur.fetchone()
print(row)
The following table shows the number of round-trips required to fetch various
numbers of rows with different ``prefetchrows`` and ``arraysize`` values.
.. list-table-with-summary:: Effect of ``prefetchrows`` and ``arraysize`` on the number of round-trips
:header-rows: 1
:class: wy-table-responsive
:align: center
:summary: The first column is the number of rows used for the example. The second column is the prefetchrows value. The third column is the arraysize value. The final column shows how many round-trips it would take to fetch all data from the database.
* - Number of rows
- ``prefetchrows``
- ``arraysize``
- Round-trips
* - 1
- 2
- 100
- 1
* - 100
- 2
- 100
- 2
* - 1000
- 2
- 100
- 11
* - 10000
- 2
- 100
- 101
* - 10000
- 2
- 1000
- 11
* - 10000
- 1000
- 1000
- 11
* - 20
- 20
- 20
- 2
* - 20
- 21
- 20
- 1
Application Default Prefetchrows and Arraysize Values
+++++++++++++++++++++++++++++++++++++++++++++++++++++
@ -257,50 +206,70 @@ a re-executed statement, create a new cursor. For example, to change
Avoiding Premature Prefetching
++++++++++++++++++++++++++++++
There are two cases that will benefit from setting ``prefetchrows`` to zero:
There are two cases that will benefit from setting ``prefetchrows`` to 0:
* When passing REF CURSORS *into* PL/SQL packages. Setting ``prefetchrows`` to
0 can stop rows being prematurely (and silently) fetched into the
python-oracledb internal buffer, making those rows unavailable to the PL/SQL
code that receives the REF CURSOR.
* When passing REF CURSORS into PL/SQL packages. Setting ``prefetchrows`` to 0
can stop rows being prematurely (and silently) fetched into the
python-oracledb or Oracle Client (in python-oracledb Thick mode) internal
buffer, making those rows unavailable to the PL/SQL code that receives the
REF CURSOR.
* When querying a PL/SQL function that uses PIPE ROW to emit rows at
intermittent intervals. By default, several rows needs to be emitted by the
function before python-oracledb can return them to the application. Setting
``prefetchrows`` to 0 helps give a consistent flow of data to the
application.
``prefetchrows`` to 0 helps give a consistent flow of data to the application.
Tuning Data Copies between Databases
------------------------------------
One place where increasing ``arraysize`` is particularly useful is in copying
data from one database to another:
.. code-block:: python
# setup cursors
source_cursor = source_connection.cursor()
source_cursor.arraysize = 1000
target_cursor = target_connection.cursor()
# perform fetch and bulk insertion
source_cursor.execute("select * from MyTable")
while True:
rows = source_cursor.fetchmany()
if not rows:
break
target_cursor.executemany("insert into MyTable values (:1, :2)", rows)
target_connection.commit()
Note that it may be preferable to use database links between the databases and
use an INSERT INTO SELECT statement so that data is not copied to, and from,
Python.
Tuning Fetching from REF CURSORS
--------------------------------
The internal buffering and performance of fetching data from REF CURSORS can be
tuned by setting the value of ``arraysize`` before rows are fetched from the
cursor. The ``prefetchrows`` value is ignored when fetching *from* REF CURSORS.
In python-oracledb, fetching data from REF CURSORS can be tuned by setting the
values of ``arraysize`` and ``prefetchrows``. The ``prefetchrows`` value must
be set before calling the PL/SQL procedure because the REF CURSOR is executed
on the server.
For example:
.. code-block:: python
# Set the arraysize and prefetch rows of the REF cursor
ref_cursor = connection.cursor()
cursor.callproc("myrefcursorproc", [ref_cursor])
ref_cursor.prefetchrows = 1000
ref_cursor.arraysize = 1000
# Perform the tuned fetch
sum_rows = 0
cursor.callproc("myrefcursorproc", [ref_cursor])
print("Sum of IntCol for", num_rows, "rows:")
for row in ref_cursor:
sum_rows += row[0]
print(sum_rows)
The ``arraysize`` value can also be set before calling the procedure:
.. code-block:: python
ref_cursor = connection.cursor()
ref_cursor.arraysize = 1000
cursor.callproc("myrefcursorproc", [ref_cursor])
for row in ref_cursor:
. . .
.. _roundtrips:
Also see `Avoiding Premature Prefetching`_.
@ -308,41 +277,31 @@ Also see `Avoiding Premature Prefetching`_.
Database Round-trips
====================
A round-trip is defined as the travel of a message from python-oracledb to the
database and back. Calling each python-oracledb function, or accessing each
attribute, will require zero or more round-trips. For example, inserting a
simple row involves sending data to the database and getting a success response
back. This is a round-trip. Along with tuning an application's architecture
and `tuning its SQL statements
A round-trip is defined as the trip from the Oracle Client libraries (used by
python-oracledb) to the database and back. Calling each python-oracledb function, or
accessing each attribute, will require zero or more round-trips. Along with
tuning an application's architecture and `tuning its SQL statements
<https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=TGSQL>`__, a general
performance and scalability goal is to minimize `round-trips
<https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-9B2F05F9-D841-
4493-A42D-A7D89694A2D1>`__ because they impact application performance and
overall system scalability.
<https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-9B2F05F9-D841-4493-A42D-A7D89694A2D1>`__.
Some general tips for reducing round-trips are:
* Tune :attr:`Cursor.arraysize` and :attr:`Cursor.prefetchrows` for each
query.
* Tune :attr:`Cursor.arraysize` and :attr:`Cursor.prefetchrows` for each query.
* Use :meth:`Cursor.executemany()` for optimal DML execution.
* Only commit when necessary. Use :attr:`Connection.autocommit` on the last
statement of a transaction.
* For connection pools, use a callback to set connection state, see
:ref:`Session Callbacks for Setting Pooled Connection State
<sessioncallback>`.
* Make use of PL/SQL procedures which execute multiple SQL statements instead
of executing them individually from python-oracledb.
* Only commit when necessary. Use :attr:`Connection.autocommit` on the last statement of a transaction.
* For connection pools, use a callback to set connection state, see :ref:`Session CallBacks for Setting Pooled Connection State <sessioncallback>`.
* Make use of PL/SQL procedures which execute multiple SQL statements instead of executing them individually from python-oracledb.
* Use scalar types instead of Oracle Database object types.
* Avoid overuse of :meth:`Connection.ping()`.
* Avoid setting :attr:`ConnectionPool.ping_interval` to 0 or a small value.
* When using :ref:`SODA <sodausermanual>`, use pooled connections and enable
the :ref:`SODA metadata cache <sodametadatacache>`.
* When using :ref:`SODA <sodausermanual>`, use pooled connections and enable the :ref:`SODA metadata cache <sodametadatacache>`.
Finding the Number of Round-Trips
----------------------------------
Oracle's `Automatic Workload Repository <https://www.oracle.com/pls/topic/
lookup?ctx=dblatest&id=GUID-56AEF38E-9400-427B-A818-EDEC145F7ACD>`__
Oracle's `Automatic Workload Repository
<https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-56AEF38E-9400-427B-A818-EDEC145F7ACD>`__
(AWR) reports show 'SQL*Net roundtrips to/from client' and are useful for
finding the overall behavior of a system.
@ -350,57 +309,26 @@ Sometimes you may wish to find the number of round-trips used for a
specific application. Snapshots of the ``V$SESSTAT`` view taken before
and after doing some work can be used for this:
.. code-block:: python
.. code-block:: sql
# Get the connection's session id
def get_session_id(connection):
sql = "select sys_context('userenv','sid') from dual"
result, = connection.cursor().execute(sql).fetchone()
return result
# Get the number of round-trips a session has made so far
def get_round_trips(systemconn, sid):
sql = """select
ss.value
from
v$sesstat ss,
v$statname sn
where
ss.sid = :sid
and ss.statistic# = sn.statistic#
and sn.name like '%roundtrip%client%'"""
round_trips, = systemconn.cursor().execute(sql, [sid]).fetchone()
return round_trips
systemconn = oracledb.connect(user="system", password=spw, dsn=cs)
connection = oracledb.connect(user=un, password=pw, dsn=cs)
sid = get_session_id(connection)
round_trips_before = get_round_trips(systemconn, sid)
# Do some "work"
cursor.execute("select ...")
rows = cursor.fetchall()
round_trips_after = get_round_trips(systemconn, sid)
print(f"Round-trips required for query: {round_trips_after - round_trips_before}")
SELECT ss.value, sn.display_name
FROM v$sesstat ss, v$statname sn
WHERE ss.sid = SYS_CONTEXT('USERENV','SID')
AND ss.statistic# = sn.statistic#
AND sn.name LIKE '%roundtrip%client%';
.. _stmtcache:
Statement Caching
=================
Python-oracledb's :meth:`Cursor.execute()` and :meth:`Cursor.executemany()`
methods use statement caching to make re-execution of statements efficient.
Statement caching lets Oracle Database cursors be used without re-parsing the
statement. Statement caching also reduces metadata transfer costs between
python-oracledb and the database. Performance and scalability are improved.
The python-oracledb Thick mode uses `Oracle Call Interface statement cache
<https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-4947CAE8-1F00-
4897-BB2B-7F921E495175>`__, whereas the Thin mode uses its own implementation.
Python-oracledb's :meth:`Cursor.execute()` and :meth:`Cursor.executemany()` functions
use the `Oracle Call Interface statement cache
<https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-4947CAE8-1F00-4897-BB2B-7F921E495175>`__
for efficient re-execution of statements. Statement caching lets Oracle
Database cursors be used without re-parsing the statement. Statement caching
also reduces metadata transfer costs between python-oracledb and the database.
Performance and scalability are improved.
Each standalone or pooled connection has its own cache of statements with a
default size of 20. The default size of the statement cache can be changed
@ -408,19 +336,18 @@ using the :attr:`defaults.stmtcachesize` attribute. The size can be set when
creating connection pools or standalone connections. In general, set the
statement cache size to the size of the working set of statements being
executed by the application. To manually tune the cache, monitor the general
application load and the `Automatic Workload Repository <https://www.oracle.
com/pls/topic/lookup?ctx=dblatest&id=GUID-56AEF38E-9400-427B-A818-
EDEC145F7ACD>`__ (AWR) "bytes sent via SQL*Net to client" values. The latter
statistic should benefit from not shipping statement metadata to
python-oracledb. Adjust the statement cache size to your satisfaction. With
Oracle Database 12c (or later), the statement cache size can be automatically
tuned using an :ref:`oraaccess.xml <optclientfiles>` file.
application load and the `Automatic Workload Repository
<https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-56AEF38E-9400-427B-A818-EDEC145F7ACD>`__
(AWR) "bytes sent via SQL*Net to client" values. The latter statistic should
benefit from not shipping statement metadata to python-oracledb. Adjust the
statement cache size to your satisfaction. With Oracle Database 12c, or later,
the statement cache size can be automatically tuned using an
:ref:`oraaccess.xml <optclientfiles>` file.
Setting the Statement Cache
---------------------------
The statement cache size can be set globally with
:attr:`defaults.stmtcachesize`:
The statement cache size can be set globally with :attr:`defaults.stmtcachesize`:
.. code-block:: python
@ -428,22 +355,20 @@ The statement cache size can be set globally with
oracledb.defaults.stmtcachesize = 40
The value can be overridden in an :meth:`oracledb.connect()` call, or when
creating a pool with :meth:`oracledb.create_pool()`. For example:
The value can be overridden in an :meth:`oracledb.connect()` call, or when creating a pool
with :meth:`oracledb.create_pool()`. For example:
.. code-block:: python
oracledb.create_pool(user="scott", password=userpwd, dsn="dbhost.example.com/orclpb",
min=2, max=5, increment=1, stmtcachesize=50)
When python-oracledb Thick mode uses Oracle Client 21 (or later), changing the
cache size with :meth:`ConnectionPool.reconfigure()` does not immediately
affect connections previously acquired and currently in use. When those
connections are subsequently released to the pool and re-acquired, they will
then use the new value. When the Thick mode uses Oracle Client prior to
version 21, changing the pool's statement cache size has no effect on
connections that already exist in the pool but will affect new connections
that are subsequently created, for example when the pool grows.
When using Oracle Client 21 (or later), changing the cache size with :meth:`ConnectionPool.reconfigure()`
does not immediately affect connections previously acquired and currently in use. When those connections
are subsequently released to the pool and re-acquired, they will then use the new value.
When using Oracle Client prior to version 21, changing the pool's statement cache size has no effect
on connections that already exist in the pool but will affect new connections that are subsequently
created, for example when the pool grows.
Tuning the Statement Cache
--------------------------
@ -453,9 +378,8 @@ statements being executed by the application. :ref:`SODA <sodausermanual>`
internally makes SQL calls, so tuning the cache is also beneficial for SODA
applications.
In python-oracledb Thick mode with Oracle Client Libraries 12c (or later), the
statement cache size can be automatically tuned with the Oracle Client
Configuration :ref:`oraaccess.xml <optclientfiles>` file.
With Oracle Client Libraries 12c, or later, the statement cache size can be automatically tuned
with the Oracle Client Configuration oraaccess.xml file.
For manual tuning use views like V$SYSSTAT:
@ -463,15 +387,12 @@ For manual tuning use views like V$SYSSTAT:
SELECT value FROM V$SYSSTAT WHERE name = 'parse count (total)'
Find the value before and after running application load to give the number of
statement parses during the load test. Alter the statement cache size and
repeat the test until you find a minimal number of parses.
Find the value before and after running application load to give the number of statement parses
during the load test. Alter the statement cache size and repeat the test until you find a minimal number of parses.
If you have Automatic Workload Repository (AWR) reports you can monitor
general application load and the "bytes sent via SQL*Net to client" values.
The latter statistic should benefit from not shipping statement metadata to
python-oracledb. Adjust the statement cache size and re-run the test to find
the best cache size.
If you have Automatic Workload Repository (AWR) reports you can monitor general application load and
the "bytes sent via SQL*Net to client" values. The latter statistic should benefit from not shipping
statement metadata to python-oracledb. Adjust the statement cache size and re-run the test to find the best cache size.
Disabling the Statement Cache
-----------------------------
@ -488,27 +409,24 @@ reused. For example if there are more distinct statements than cache
slots, and the order of statement execution causes older statements to
be flushed from the cache before the statements are re-executed.
Disabling the statement cache may also be helpful in test and development
environments. The statement cache can become invalid if connections remain
open and database schema objects are recreated. Applications can then receive
errors such as ``ORA-3106``. However, after a statement execution error is
returned once to the application, python-oracledb automatically drops that
statement from the cache. This lets subsequent re-executions of the statement
on that connection to succeed.
Disabling the statement cache may also be helpful in test and development environments.
The statement cache can become invalid if connections remain open and database schema
objects are recreated. This can also happen when a connection uses identical query text
with different ``fetchAsString`` or ``fetchInfo`` data types. Applications can receive
errors such as ORA-3106. After a statement execution error is returned once to the application,
python-oracledb automatically drops that statement from the cache. This lets subsequent
re-executions of the statement on that connection to succeed.
When it is inconvenient to pass statement text through an application, the
:meth:`Cursor.prepare()` call can be used to avoid statement re-parsing.
If the ``cache_statement`` parameter in the :meth:`Cursor.prepare()` method is
True and the statement cache size is greater than 0, then the statements will
be added to the cache, if not already present. If the ``cache_statement``
parameter in the :meth:`Cursor.prepare()` method is False and the statement
cache size is greater than 0, then the statement will be removed from the
statement cache (if present) or will not be cached (if not present). The
subsequent ``execute()`` calls use the value None instead of the SQL text.
If the ``cache_statement`` parameter in the :meth:`Cursor.prepare()` method is True and the statement cache size
is greater than 0, then the statements will be added to the cache, if not already present.
If the ``cache_statement`` parameter in the :meth:`Cursor.prepare()` method is False and the statement cache size
is greater than 0, then the statement will be removed from the statement cache (if present)
or will not be cached (if not present). The subsequent ``execute()`` calls use the value None instead of the SQL text.
This feature can prevent a rarely executed statement from flushing a potential
more frequently executed one from a full cache. For example, if a statement
will only ever be executed once:
This feature can prevent a rarely executed statement from flushing a potential more frequently executed one from a full cache.
For example, if a statement will only ever be executed once:
.. code-block:: python
@ -532,25 +450,24 @@ Client Result Caching (CRC)
===========================
Python-oracledb applications can use Oracle Database's `Client Result Cache
<https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-35CB2592-7588-
4C2D-9075-6F639F25425E>`__. The CRC enables client-side caching of SQL query
(SELECT statement) results in client memory for immediate use when the same
query is re-executed. This is useful for reducing the cost of queries for
small, mostly static, lookup tables, such as for postal codes. CRC reduces
network :ref:`round-trips <roundtrips>`, and also reduces database server CPU
usage.
<https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-35CB2592-7588-4C2D-9075-6F639F25425E>`__.
The CRC enables client-side caching of SQL query (SELECT statement) results in
client memory for immediate use when the same query is re-executed. This is
useful for reducing the cost of queries for small, mostly static, lookup tables,
such as for postal codes. CRC reduces network :ref:`round-trips <roundtrips>`,
and also reduces database server CPU usage.
.. note::
Client Result Caching is only supported in the python-oracledb Thick mode.
See :ref:`enablingthick`.
Client Result Caching is only supported in the python-oracledb Thick mode.
See :ref:`enablingthick`.
The cache is at the application process level. Access and invalidation is
managed by the Oracle Client libraries. This removes the need for extra
application logic, or external utilities, to implement a cache.
CRC can be enabled by setting the `database parameters <https://www.oracle.com
/pls/topic/lookup?ctx=dblatest&id=GUID-A9D4A5F5-B939-48FF-80AE-0228E7314C7D>`__
CRC can be enabled by setting the `database parameters
<https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-A9D4A5F5-B939-48FF-80AE-0228E7314C7D>`__
``CLIENT_RESULT_CACHE_SIZE`` and ``CLIENT_RESULT_CACHE_LAG``, and then
restarting the database, for example:
@ -562,12 +479,11 @@ restarting the database, for example:
CRC can alternatively be configured in an :ref:`oraaccess.xml <optclientfiles>`
or :ref:`sqlnet.ora <optnetfiles>` file on the Python host, see `Client
Configuration Parameters <https://www.oracle.com/pls/topic/lookup?ctx=dblatest
&id=GUID-E63D75A1-FCAA-4A54-A3D2-B068442CE766>`__.
Configuration Parameters
<https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-E63D75A1-FCAA-4A54-A3D2-B068442CE766>`__.
Tables can then be created, or altered, so repeated queries use CRC. This
allows existing applications to use CRC without needing modification. For
example:
allows existing applications to use CRC without needing modification. For example:
.. code-block:: sql

View File

@ -7,11 +7,11 @@ Managing Transactions
A database transaction is a grouping of SQL statements that make a logical data
change to the database.
When :meth:`Cursor.execute()` or :meth:`Cursor.executemany()` executes a SQL
statement, a transaction is started or continued. By default, python-oracledb
does not commit this transaction to the database. The methods
:meth:`Connection.commit()` and :meth:`Connection.rollback()` methods can be
used to explicitly commit or rollback a transaction:
When :meth:`Cursor.execute()` executes a SQL statement, a transaction is
started or continued. By default, python-oracledb does not commit this transaction
to the database. The methods :meth:`Connection.commit()` and
:meth:`Connection.rollback()` methods can be used to explicitly commit
or rollback a transaction:
.. code-block:: python

View File

@ -1,13 +1,3 @@
[build-system]
requires = ["setuptools >= 40.6.0", "wheel", "cython"]
build-backend = "setuptools.build_meta"
[tool.black]
line-length = 79
target-version = ["py37", "py38", "py39", "py310", "py311", "py312"]
required-version = 23
[tool.ruff]
line-length = 79
target-version = "py38"
exclude = ["templates"]

View File

@ -1,9 +1,5 @@
## Python-oracledb Examples
This directory contains samples for python-oracledb.
### Basic Examples
1. The schemas and SQL objects that are referenced in the samples can be
created by running the Python script [create_schema.py][1]. The script
requires SYSDBA privileges and will prompt for these credentials as well as
@ -26,16 +22,6 @@ This directory contains samples for python-oracledb.
python drop_schema.py
### Examples in a Container
The [sample_container](./sample_container) directory has a Dockerfile that will
build a container with the samples and a running Oracle Database.
### Notebooks
The [sample_notebooks](./sample_notebooks) directory has Jupyter notebooks with
runnable examples.
[1]: https://github.com/oracle/python-oracledb/blob/main/samples/create_schema.py
[2]: https://github.com/oracle/python-oracledb/blob/main/samples/sample_env.py
[3]: https://github.com/oracle/python-oracledb/blob/main/samples/drop_schema.py

View File

@ -1,5 +1,5 @@
# -----------------------------------------------------------------------------
# Copyright (c) 2016, 2023, Oracle and/or its affiliates.
#------------------------------------------------------------------------------
# Copyright (c) 2016, 2022, Oracle and/or its affiliates.
#
# Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved.
#
@ -25,15 +25,15 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# -----------------------------------------------------------------------------
#------------------------------------------------------------------------------
# -----------------------------------------------------------------------------
#------------------------------------------------------------------------------
# app_context.py
#
# Demonstrates the use of application context. Application context is available
# within logon triggers and can be retrieved by using the function
# sys_context().
# -----------------------------------------------------------------------------
#------------------------------------------------------------------------------
import oracledb
import sample_env
@ -44,22 +44,20 @@ oracledb.init_oracle_client(lib_dir=sample_env.get_oracle_client())
# client context attributes to be set
APP_CTX_NAMESPACE = "CLIENTCONTEXT"
APP_CTX_ENTRIES = [
(APP_CTX_NAMESPACE, "ATTR1", "VALUE1"),
(APP_CTX_NAMESPACE, "ATTR2", "VALUE2"),
(APP_CTX_NAMESPACE, "ATTR3", "VALUE3"),
( APP_CTX_NAMESPACE, "ATTR1", "VALUE1" ),
( APP_CTX_NAMESPACE, "ATTR2", "VALUE2" ),
( APP_CTX_NAMESPACE, "ATTR3", "VALUE3" )
]
connection = oracledb.connect(
user=sample_env.get_main_user(),
password=sample_env.get_main_password(),
dsn=sample_env.get_connect_string(),
appcontext=APP_CTX_ENTRIES,
)
connection = oracledb.connect(user=sample_env.get_main_user(),
password=sample_env.get_main_password(),
dsn=sample_env.get_connect_string(),
appcontext=APP_CTX_ENTRIES)
with connection.cursor() as cursor:
for namespace, name, value in APP_CTX_ENTRIES:
cursor.execute(
"select sys_context(:1, :2) from dual", (namespace, name)
)
(value,) = cursor.fetchone()
cursor.execute("select sys_context(:1, :2) from dual",
(namespace, name))
value, = cursor.fetchone()
print("Value of context key", name, "is", value)

View File

@ -1,5 +1,5 @@
# -----------------------------------------------------------------------------
# Copyright (c) 2018, 2023, Oracle and/or its affiliates.
#------------------------------------------------------------------------------
# Copyright (c) 2018, 2022, Oracle and/or its affiliates.
#
# This software is dual-licensed to you under the Universal Permissive License
# (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl and Apache License
@ -20,15 +20,15 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# -----------------------------------------------------------------------------
#------------------------------------------------------------------------------
# -----------------------------------------------------------------------------
#------------------------------------------------------------------------------
# aq_notification.py
#
# Demonstrates using advanced queuing notification. Once this script is
# running, run object_aq.py in another terminal to enqueue a few messages to
# the "DEMO_BOOK_QUEUE" queue.
# -----------------------------------------------------------------------------
#------------------------------------------------------------------------------
import time
@ -40,7 +40,6 @@ oracledb.init_oracle_client(lib_dir=sample_env.get_oracle_client())
registered = True
def process_messages(message):
global registered
print("Message type:", message.type)
@ -52,20 +51,14 @@ def process_messages(message):
print("Consumer name:", message.consumer_name)
print("Message id:", message.msgid)
connection = oracledb.connect(user=sample_env.get_main_user(),
password=sample_env.get_main_password(),
dsn=sample_env.get_connect_string(),
events=True)
connection = oracledb.connect(
user=sample_env.get_main_user(),
password=sample_env.get_main_password(),
dsn=sample_env.get_connect_string(),
events=True,
)
sub = connection.subscribe(
namespace=oracledb.SUBSCR_NAMESPACE_AQ,
name="DEMO_BOOK_QUEUE",
callback=process_messages,
timeout=300,
)
sub = connection.subscribe(namespace=oracledb.SUBSCR_NAMESPACE_AQ,
name="DEMO_BOOK_QUEUE", callback=process_messages,
timeout=300)
print("Subscription:", sub)
print("--> Connection:", sub.connection)
print("--> Callback:", sub.callback)

View File

@ -1,5 +1,5 @@
# -----------------------------------------------------------------------------
# Copyright (c) 2016, 2023, Oracle and/or its affiliates.
#------------------------------------------------------------------------------
# Copyright (c) 2016, 2022, Oracle and/or its affiliates.
#
# This software is dual-licensed to you under the Universal Permissive License
# (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl and Apache License
@ -20,16 +20,16 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# -----------------------------------------------------------------------------
#------------------------------------------------------------------------------
# -----------------------------------------------------------------------------
#------------------------------------------------------------------------------
# array_dml_rowcounts.py
#
# Demonstrates the use of the 12.1 feature that allows cursor.executemany()
# to return the number of rows affected by each individual execution as a list.
# The parameter "arraydmlrowcounts" must be set to True in the call to
# cursor.executemany() after which cursor.getarraydmlrowcounts() can be called.
# -----------------------------------------------------------------------------
#------------------------------------------------------------------------------
import oracledb
import sample_env
@ -38,23 +38,19 @@ import sample_env
if not sample_env.get_is_thin():
oracledb.init_oracle_client(lib_dir=sample_env.get_oracle_client())
connection = oracledb.connect(
user=sample_env.get_main_user(),
password=sample_env.get_main_password(),
dsn=sample_env.get_connect_string(),
)
connection = oracledb.connect(user=sample_env.get_main_user(),
password=sample_env.get_main_password(),
dsn=sample_env.get_connect_string())
with connection.cursor() as cursor:
# show the number of rows for each parent ID as a means of verifying the
# output from the delete statement
for parent_id, count in cursor.execute(
"""
select ParentId, count(*)
from ChildTable
group by ParentId
order by ParentId
"""
):
for parent_id, count in cursor.execute("""
select ParentId, count(*)
from ChildTable
group by ParentId
order by ParentId"""):
print("Parent ID:", parent_id, "has", int(count), "rows.")
print()
@ -65,11 +61,11 @@ with connection.cursor() as cursor:
print()
# enable array DML row counts for each iteration executed in executemany()
cursor.executemany(
"delete from ChildTable where ParentId = :1",
[(i,) for i in parent_ids_to_delete],
arraydmlrowcounts=True,
)
cursor.executemany("""
delete from ChildTable
where ParentId = :1""",
[(i,) for i in parent_ids_to_delete],
arraydmlrowcounts = True)
# display the number of rows deleted for each parent ID
row_counts = cursor.getarraydmlrowcounts()

View File

@ -1,84 +0,0 @@
# -----------------------------------------------------------------------------
# Copyright (c) 2023, Oracle and/or its affiliates.
#
# This software is dual-licensed to you under the Universal Permissive License
# (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl and Apache License
# 2.0 as shown at http://www.apache.org/licenses/LICENSE-2.0. You may choose
# either license.
#
# If you elect to accept the software under the Apache License, Version 2.0,
# the following applies:
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# -----------------------------------------------------------------------------
# -----------------------------------------------------------------------------
# array_dml_rowcounts_async.py
#
# An asynchronous version of array_dml_rowcounts.py
#
# Demonstrates the use of the 12.1 feature that allows cursor.executemany()
# to return the number of rows affected by each individual execution as a list.
# The parameter "arraydmlrowcounts" must be set to True in the call to
# cursor.executemany() after which cursor.getarraydmlrowcounts() can be called.
# -----------------------------------------------------------------------------
import asyncio
import oracledb
import sample_env
async def main():
connection = await oracledb.connect_async(
user=sample_env.get_main_user(),
password=sample_env.get_main_password(),
dsn=sample_env.get_connect_string(),
)
with connection.cursor() as cursor:
# show the number of rows for each parent ID as a means of verifying
# the output from the delete statement
await cursor.execute(
"""
select ParentId, count(*)
from ChildTable
group by ParentId
order by ParentId
"""
)
async for parent_id, count in cursor:
print("Parent ID:", parent_id, "has", int(count), "rows.")
print()
# delete the following parent IDs only
parent_ids_to_delete = [20, 30, 50]
print("Deleting Parent IDs:", parent_ids_to_delete)
print()
# enable array DML row counts for each iteration executed in
# executemany()
await cursor.executemany(
"delete from ChildTable where ParentId = :1",
[(i,) for i in parent_ids_to_delete],
arraydmlrowcounts=True,
)
# display the number of rows deleted for each parent ID
row_counts = cursor.getarraydmlrowcounts()
for parent_id, count in zip(parent_ids_to_delete, row_counts):
print("Parent ID:", parent_id, "deleted", count, "rows.")
asyncio.run(main())

View File

@ -1,81 +0,0 @@
# -----------------------------------------------------------------------------
# Copyright (c) 2023, Oracle and/or its affiliates.
#
# This software is dual-licensed to you under the Universal Permissive License
# (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl and Apache License
# 2.0 as shown at http://www.apache.org/licenses/LICENSE-2.0. You may choose
# either license.
#
# If you elect to accept the software under the Apache License, Version 2.0,
# the following applies:
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# -----------------------------------------------------------------------------
# -----------------------------------------------------------------------------
# async_gather.py
#
# Demonstrates using a connection pool with asyncio and gather().
#
# Multiple database sessions will be opened and used by each coroutine. The
# number of connections opened can depend on the speed of your environment.
# -----------------------------------------------------------------------------
import asyncio
import oracledb
import sample_env
# Number of coroutines to run
CONCURRENCY = 5
# Query the unique session identifier/serial number combination of a connection
SQL = """SELECT UNIQUE CURRENT_TIMESTAMP AS CT, sid||'-'||serial# AS SIDSER
FROM v$session_connect_info
WHERE sid = SYS_CONTEXT('USERENV', 'SID')"""
# Show the unique session identifier/serial number of each connection that the
# pool opens
async def init_session(connection, requested_tag):
res = await connection.fetchone(SQL)
print(res[0].strftime("%H:%M:%S.%f"), "- init_session SID-SERIAL#", res[1])
# The coroutine simply shows the session identifier/serial number of the
# connection returned by the pool.acquire() call
async def query(pool):
async with pool.acquire() as connection:
await connection.callproc("dbms_session.sleep", [1])
res = await connection.fetchone(SQL)
print(res[0].strftime("%H:%M:%S.%f"), "- query SID-SERIAL#", res[1])
async def main():
pool = oracledb.create_pool_async(
user=sample_env.get_main_user(),
password=sample_env.get_main_password(),
dsn=sample_env.get_connect_string(),
min=1,
max=CONCURRENCY,
session_callback=init_session,
)
coroutines = [query(pool) for i in range(CONCURRENCY)]
await asyncio.gather(*coroutines)
await pool.close()
asyncio.run(main())

View File

@ -1,5 +1,5 @@
# -----------------------------------------------------------------------------
# Copyright (c) 2016, 2023, Oracle and/or its affiliates.
#------------------------------------------------------------------------------
# Copyright (c) 2016, 2022, Oracle and/or its affiliates.
#
# This software is dual-licensed to you under the Universal Permissive License
# (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl and Apache License
@ -20,9 +20,9 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# -----------------------------------------------------------------------------
#------------------------------------------------------------------------------
# -----------------------------------------------------------------------------
#------------------------------------------------------------------------------
# batch_errors.py
#
# Demonstrates the use of the Oracle Database 12.1 feature that allows
@ -31,7 +31,7 @@
# executions. The parameter "batcherrors" must be set to True in the
# call to cursor.executemany() after which cursor.getbatcherrors() can
# be called, which will return a list of error objects.
# -----------------------------------------------------------------------------
#------------------------------------------------------------------------------
import oracledb
import sample_env
@ -40,28 +40,29 @@ import sample_env
if not sample_env.get_is_thin():
oracledb.init_oracle_client(lib_dir=sample_env.get_oracle_client())
connection = oracledb.connect(
user=sample_env.get_main_user(),
password=sample_env.get_main_password(),
dsn=sample_env.get_connect_string(),
)
connection = oracledb.connect(user=sample_env.get_main_user(),
password=sample_env.get_main_password(),
dsn=sample_env.get_connect_string())
with connection.cursor() as cursor:
# retrieve the number of rows in the table
cursor.execute("select count(*) from ChildTable")
(count,) = cursor.fetchone()
cursor.execute("""
select count(*)
from ChildTable""")
count, = cursor.fetchone()
print("Number of rows in child table:", int(count))
# define data to insert
data_to_insert = [
(1016, 10, "Child B of Parent 10"),
(1017, 10, "Child C of Parent 10"),
(1018, 20, "Child D of Parent 20"),
(1018, 20, "Child D of Parent 20"), # duplicate key
(1019, 30, "Child C of Parent 30"),
(1020, 30, "Child D of Parent 40"),
(1021, 60, "Child A of Parent 60"), # parent does not exist
(1022, 40, "Child F of Parent 40"),
(1016, 10, 'Child B of Parent 10'),
(1017, 10, 'Child C of Parent 10'),
(1018, 20, 'Child D of Parent 20'),
(1018, 20, 'Child D of Parent 20'), # duplicate key
(1019, 30, 'Child C of Parent 30'),
(1020, 30, 'Child D of Parent 40'),
(1021, 60, 'Child A of Parent 60'), # parent does not exist
(1022, 40, 'Child F of Parent 40'),
]
print("Number of rows to insert:", len(data_to_insert))
@ -69,17 +70,18 @@ with connection.cursor() as cursor:
# first error takes place; the row count is updated to show how many rows
# actually succeeded
try:
cursor.executemany(
"insert into ChildTable values (:1, :2, :3)", data_to_insert
)
cursor.executemany("insert into ChildTable values (:1, :2, :3)",
data_to_insert)
except oracledb.DatabaseError as e:
(error,) = e.args
error, = e.args
print("Failure with error:", error.message)
print("Number of rows successfully inserted:", cursor.rowcount)
# demonstrate that the row count is accurate
cursor.execute("select count(*) from ChildTable")
(count,) = cursor.fetchone()
cursor.execute("""
select count(*)
from ChildTable""")
count, = cursor.fetchone()
print("Number of rows in child table after failed insert:", int(count))
# roll back so we can perform the same work using the new method
@ -87,12 +89,9 @@ with connection.cursor() as cursor:
# new method: executemany() with batch errors enabled (and array DML row
# counts also enabled) results in no immediate error being raised
cursor.executemany(
"insert into ChildTable values (:1, :2, :3)",
data_to_insert,
batcherrors=True,
arraydmlrowcounts=True,
)
cursor.executemany("insert into ChildTable values (:1, :2, :3)",
data_to_insert, batcherrors=True,
arraydmlrowcounts=True)
# display the errors that have taken place
errors = cursor.getbatcherrors()
@ -107,10 +106,9 @@ with connection.cursor() as cursor:
# demonstrate that all of the rows without errors have been successfully
# inserted
cursor.execute("select count(*) from ChildTable")
(count,) = cursor.fetchone()
print(
"Number of rows in child table after insert with batcherrors "
"enabled:",
int(count),
)
cursor.execute("""
select count(*)
from ChildTable""")
count, = cursor.fetchone()
print("Number of rows in child table after insert with batcherrors "
"enabled:", int(count))

View File

@ -1,123 +0,0 @@
# -----------------------------------------------------------------------------
# Copyright (c) 2023, Oracle and/or its affiliates.
#
# This software is dual-licensed to you under the Universal Permissive License
# (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl and Apache License
# 2.0 as shown at http://www.apache.org/licenses/LICENSE-2.0. You may choose
# either license.
#
# If you elect to accept the software under the Apache License, Version 2.0,
# the following applies:
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# -----------------------------------------------------------------------------
# -----------------------------------------------------------------------------
# batch_errors_async.py
#
# An asynchronous version of batch_errors.py
#
# Demonstrates the use of the Oracle Database 12.1 feature that allows
# cursor.executemany() to complete successfully, even if errors take
# place during the execution of one or more of the individual
# executions. The parameter "batcherrors" must be set to True in the
# call to cursor.executemany() after which cursor.getbatcherrors() can
# be called, which will return a list of error objects.
# -----------------------------------------------------------------------------
import asyncio
import oracledb
import sample_env
async def main():
connection = await oracledb.connect_async(
user=sample_env.get_main_user(),
password=sample_env.get_main_password(),
dsn=sample_env.get_connect_string(),
)
with connection.cursor() as cursor:
# retrieve the number of rows in the table
await cursor.execute("select count(*) from ChildTable")
(count,) = await cursor.fetchone()
print("Number of rows in child table:", int(count))
# define data to insert
data_to_insert = [
(1016, 10, "Child B of Parent 10"),
(1017, 10, "Child C of Parent 10"),
(1018, 20, "Child D of Parent 20"),
(1018, 20, "Child D of Parent 20"), # duplicate key
(1019, 30, "Child C of Parent 30"),
(1020, 30, "Child D of Parent 40"),
(1021, 60, "Child A of Parent 60"), # parent does not exist
(1022, 40, "Child F of Parent 40"),
]
print("Number of rows to insert:", len(data_to_insert))
# old method: executemany() with data errors results in stoppage after
# the first error takes place; the row count is updated to show how
# many rows actually succeeded
try:
await cursor.executemany(
"insert into ChildTable values (:1, :2, :3)", data_to_insert
)
except oracledb.DatabaseError as e:
(error,) = e.args
print("Failure with error:", error.message)
print("Number of rows successfully inserted:", cursor.rowcount)
# demonstrate that the row count is accurate
await cursor.execute("select count(*) from ChildTable")
(count,) = await cursor.fetchone()
print("Number of rows in child table after failed insert:", int(count))
# roll back so we can perform the same work using the new method
await connection.rollback()
# new method: executemany() with batch errors enabled (and array DML
# row counts also enabled) results in no immediate error being raised
await cursor.executemany(
"insert into ChildTable values (:1, :2, :3)",
data_to_insert,
batcherrors=True,
arraydmlrowcounts=True,
)
# display the errors that have taken place
errors = cursor.getbatcherrors()
print("Number of rows with bad values:", len(errors))
for error in errors:
print(
"Error", error.message.rstrip(), "at row offset", error.offset
)
# arraydmlrowcounts also shows rows with invalid data: they have a row
# count of 0; otherwise 1 is shown
row_counts = cursor.getarraydmlrowcounts()
print("Array DML row counts:", row_counts)
# demonstrate that all of the rows without errors have been
# successfully inserted
await cursor.execute("select count(*) from ChildTable")
(count,) = await cursor.fetchone()
print(
"Number of rows in child table after insert with batcherrors "
"enabled:",
int(count),
)
asyncio.run(main())

View File

@ -1,5 +1,5 @@
# -----------------------------------------------------------------------------
# Copyright (c) 2016, 2023, Oracle and/or its affiliates.
#------------------------------------------------------------------------------
# Copyright (c) 2016, 2022, Oracle and/or its affiliates.
#
# This software is dual-licensed to you under the Universal Permissive License
# (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl and Apache License
@ -20,13 +20,13 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# -----------------------------------------------------------------------------
#------------------------------------------------------------------------------
# -----------------------------------------------------------------------------
#------------------------------------------------------------------------------
# bind_insert.py
#
# Demonstrates how to insert rows into a table using bind variables.
# -----------------------------------------------------------------------------
#------------------------------------------------------------------------------
import oracledb
import sample_env
@ -35,27 +35,26 @@ import sample_env
if not sample_env.get_is_thin():
oracledb.init_oracle_client(lib_dir=sample_env.get_oracle_client())
connection = oracledb.connect(
user=sample_env.get_main_user(),
password=sample_env.get_main_password(),
dsn=sample_env.get_connect_string(),
)
connection = oracledb.connect(user=sample_env.get_main_user(),
password=sample_env.get_main_password(),
dsn=sample_env.get_connect_string())
# -----------------------------------------------------------------------------
#------------------------------------------------------------------------------
# "Bind by position"
# -----------------------------------------------------------------------------
#------------------------------------------------------------------------------
rows = [
(1, "First"),
(2, "Second"),
(3, "Third"),
(4, "Fourth"),
(5, None), # Insert a NULL value
(5, None), # Insert a NULL value
(6, "Sixth"),
(7, "Seventh"),
(7, "Seventh")
]
with connection.cursor() as cursor:
# predefine the maximum string size to avoid data scans and memory
# reallocations. The value 'None' indicates that the default processing
# can take place
@ -63,29 +62,33 @@ with connection.cursor() as cursor:
cursor.executemany("insert into mytab(id, data) values (:1, :2)", rows)
# -----------------------------------------------------------------------------
#------------------------------------------------------------------------------
# "Bind by name"
# -----------------------------------------------------------------------------
#------------------------------------------------------------------------------
rows = [
{"d": "Eighth", "i": 8},
{"d": "Ninth", "i": 9},
{"d": "Tenth", "i": 10},
{"i": 11}, # Insert a NULL value
{"d": "Ninth", "i": 9},
{"d": "Tenth", "i": 10},
{"i": 11} # Insert a NULL value
]
with connection.cursor() as cursor:
# Predefine maximum string size to avoid data scans and memory
# reallocations
cursor.setinputsizes(d=20)
cursor.executemany("insert into mytab(id, data) values (:i, :d)", rows)
# -----------------------------------------------------------------------------
#------------------------------------------------------------------------------
# Inserting a single bind still needs tuples
# -----------------------------------------------------------------------------
#------------------------------------------------------------------------------
rows = [("Eleventh",), ("Twelth",)]
rows = [
("Eleventh",),
("Twelth",)
]
with connection.cursor() as cursor:
cursor.executemany("insert into mytab(id, data) values (12, :1)", rows)
@ -93,9 +96,9 @@ with connection.cursor() as cursor:
# Don't commit - this lets the demo be run multiple times
# connection.commit()
# -----------------------------------------------------------------------------
#------------------------------------------------------------------------------
# Now query the results back
# -----------------------------------------------------------------------------
#------------------------------------------------------------------------------
with connection.cursor() as cursor:
for row in cursor.execute("select * from mytab order by id"):

View File

@ -1,111 +0,0 @@
# -----------------------------------------------------------------------------
# Copyright (c) 2023, Oracle and/or its affiliates.
#
# This software is dual-licensed to you under the Universal Permissive License
# (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl and Apache License
# 2.0 as shown at http://www.apache.org/licenses/LICENSE-2.0. You may choose
# either license.
#
# If you elect to accept the software under the Apache License, Version 2.0,
# the following applies:
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# -----------------------------------------------------------------------------
# -----------------------------------------------------------------------------
# bind_insert_async.py
#
# An asynchronous version of bind_insert.py
#
# Demonstrates how to insert rows into a table using bind variables.
# -----------------------------------------------------------------------------
import asyncio
import oracledb
import sample_env
async def main():
connection = await oracledb.connect_async(
user=sample_env.get_main_user(),
password=sample_env.get_main_password(),
dsn=sample_env.get_connect_string(),
)
# -------------------------------------------------------------------------
# "Bind by position"
# -------------------------------------------------------------------------
rows = [
(1, "First"),
(2, "Second"),
(3, "Third"),
(4, "Fourth"),
(5, None), # Insert a NULL value
(6, "Sixth"),
(7, "Seventh"),
]
with connection.cursor() as cursor:
# predefine the maximum string size to avoid data scans and memory
# reallocations. The value 'None' indicates that the default
# processing can take place
cursor.setinputsizes(None, 20)
await cursor.executemany(
"insert into mytab(id, data) values (:1, :2)", rows
)
# -------------------------------------------------------------------------
# "Bind by name"
# -------------------------------------------------------------------------
rows = [
{"d": "Eighth", "i": 8},
{"d": "Ninth", "i": 9},
{"d": "Tenth", "i": 10},
{"i": 11}, # Insert a NULL value
]
with connection.cursor() as cursor:
# Predefine maximum string size to avoid data scans and memory
# reallocations
cursor.setinputsizes(d=20)
await cursor.executemany(
"insert into mytab(id, data) values (:i, :d)", rows
)
# -------------------------------------------------------------------------
# Inserting a single bind still needs tuples
# -------------------------------------------------------------------------
rows = [("Eleventh",), ("Twelth",)]
await connection.executemany(
"insert into mytab(id, data) values (12, :1)", rows
)
# Don't commit - this lets the demo be run multiple times
# await connection.commit()
# -------------------------------------------------------------------------
# Now query the results back
# -------------------------------------------------------------------------
for row in await connection.fetchall("select * from mytab order by id"):
print(row)
asyncio.run(main())

View File

@ -1,5 +1,5 @@
# -----------------------------------------------------------------------------
# Copyright (c) 2016, 2023, Oracle and/or its affiliates.
#------------------------------------------------------------------------------
# Copyright (c) 2016, 2022, Oracle and/or its affiliates.
#
# This software is dual-licensed to you under the Universal Permissive License
# (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl and Apache License
@ -20,9 +20,9 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# -----------------------------------------------------------------------------
#------------------------------------------------------------------------------
# -----------------------------------------------------------------------------
#------------------------------------------------------------------------------
# bind_query.py
#
# Demonstrates the use of bind variables in queries. Binding is important for
@ -31,7 +31,7 @@
# increasing performance. It also permits data to be bound without having to be
# concerned about escaping special characters, or be concerned about SQL
# injection attacks.
# -----------------------------------------------------------------------------
##------------------------------------------------------------------------------
import oracledb
import sample_env
@ -40,22 +40,21 @@ import sample_env
if not sample_env.get_is_thin():
oracledb.init_oracle_client(lib_dir=sample_env.get_oracle_client())
connection = oracledb.connect(
user=sample_env.get_main_user(),
password=sample_env.get_main_password(),
dsn=sample_env.get_connect_string(),
)
connection = oracledb.connect(user=sample_env.get_main_user(),
password=sample_env.get_main_password(),
dsn=sample_env.get_connect_string())
# Bind by position with lists
with connection.cursor() as cursor:
print("1. Bind by position: single value list")
sql = "select * from SampleQueryTab where id = :bvid"
sql = 'select * from SampleQueryTab where id = :bvid'
for row in cursor.execute(sql, [1]):
print(row)
print()
print("2. Bind by position: multiple values")
sql = "select * from SampleQueryTab where id = :bvid and 123 = :otherbind"
sql = 'select * from SampleQueryTab where id = :bvid and 123 = :otherbind'
for row in cursor.execute(sql, [2, 123]):
print(row)
print()
@ -64,10 +63,8 @@ with connection.cursor() as cursor:
# order of the placeholders used in the SQL statement. The bind list data
# order is not associated by the name of the bind variable placeholders in
# the SQL statement, even though those names are ":1" and ":2".
print(
"3. Bind by position: multiple values with numeric placeholder names"
)
sql = "select * from SampleQueryTab where id = :2 and 456 = :1"
print("3. Bind by position: multiple values with numeric placeholder names")
sql = 'select * from SampleQueryTab where id = :2 and 456 = :1'
for row in cursor.execute(sql, [3, 456]):
print(row)
print()
@ -75,7 +72,7 @@ with connection.cursor() as cursor:
# With bind-by-position, repeated use of bind placeholder names in the SQL
# statement requires the input list data to be repeated.
print("4. Bind by position: multiple values with a repeated placeholder")
sql = "select * from SampleQueryTab where id = :2 and 3 = :2"
sql = 'select * from SampleQueryTab where id = :2 and 3 = :2'
for row in cursor.execute(sql, [3, 3]):
print(row)
print()
@ -83,22 +80,24 @@ with connection.cursor() as cursor:
# Bind by position with tuples
with connection.cursor() as cursor:
print("5. Bind by position with single value tuple")
sql = "select * from SampleQueryTab where id = :bvid"
sql = 'select * from SampleQueryTab where id = :bvid'
for row in cursor.execute(sql, (4,)):
print(row)
print()
print("6. Bind by position with a multiple value tuple")
sql = "select * from SampleQueryTab where id = :bvid and 789 = :otherbind"
for row in cursor.execute(sql, (4, 789)):
sql = 'select * from SampleQueryTab where id = :bvid and 789 = :otherbind'
for row in cursor.execute(sql, (4,789)):
print(row)
print()
# Bind by name with a dictionary
with connection.cursor() as cursor:
print("7. Bind by name with a dictionary")
sql = "select * from SampleQueryTab where id = :bvid"
sql = 'select * from SampleQueryTab where id = :bvid'
for row in cursor.execute(sql, {"bvid": 4}):
print(row)
print()
@ -106,7 +105,7 @@ with connection.cursor() as cursor:
# With bind-by-name, repeated use of bind placeholder names in the SQL
# statement lets you supply the data once.
print("8. Bind by name with multiple value dict and repeated placeholders")
sql = "select * from SampleQueryTab where id = :bvid and 4 = :bvid"
sql = 'select * from SampleQueryTab where id = :bvid and 4 = :bvid'
for row in cursor.execute(sql, {"bvid": 4}):
print(row)
print()
@ -114,14 +113,15 @@ with connection.cursor() as cursor:
# Bind by name with parameters. The execute() parameter names match the bind
# variable placeholder names.
with connection.cursor() as cursor:
print("9. Bind by name using parameters")
sql = "select * from SampleQueryTab where id = :bvid"
sql = 'select * from SampleQueryTab where id = :bvid'
for row in cursor.execute(sql, bvid=5):
print(row)
print()
print("10. Bind by name using multiple parameters")
sql = "select * from SampleQueryTab where id = :bvid and 101 = :otherbind"
sql = 'select * from SampleQueryTab where id = :bvid and 101 = :otherbind'
for row in cursor.execute(sql, bvid=5, otherbind=101):
print(row)
print()
@ -129,14 +129,14 @@ with connection.cursor() as cursor:
# With bind-by-name, repeated use of bind placeholder names in the SQL
# statement lets you supply the data once.
print("11. Bind by name: multiple values with repeated placeholder names")
sql = "select * from SampleQueryTab where id = :bvid and 6 = :bvid"
sql = 'select * from SampleQueryTab where id = :bvid and 6 = :bvid'
for row in cursor.execute(sql, bvid=6):
print(row)
print()
# Rexcuting a query with different data values
with connection.cursor() as cursor:
sql = "select * from SampleQueryTab where id = :bvid"
sql = 'select * from SampleQueryTab where id = :bvid'
print("12. Query results with id = 7")
for row in cursor.execute(sql, [4]):

View File

@ -1,136 +0,0 @@
# -----------------------------------------------------------------------------
# Copyright (c) 2023, Oracle and/or its affiliates.
#
# This software is dual-licensed to you under the Universal Permissive License
# (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl and Apache License
# 2.0 as shown at http://www.apache.org/licenses/LICENSE-2.0. You may choose
# either license.
#
# If you elect to accept the software under the Apache License, Version 2.0,
# the following applies:
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# -----------------------------------------------------------------------------
# -----------------------------------------------------------------------------
# bind_query_async.py
#
# An asynchronous version of bind_query.py
#
# Demonstrates the use of bind variables in queries. Binding is important for
# scalability and security. Since the text of a query that is re-executed is
# unchanged, no additional parsing is required, thereby reducing overhead and
# increasing performance. It also permits data to be bound without having to be
# concerned about escaping special characters, or be concerned about SQL
# injection attacks.
# -----------------------------------------------------------------------------
import asyncio
import oracledb
import sample_env
async def main():
connection = await oracledb.connect_async(
user=sample_env.get_main_user(),
password=sample_env.get_main_password(),
dsn=sample_env.get_connect_string(),
)
# Bind by position with lists
print("1. Bind by position: single value list")
sql = "select * from SampleQueryTab where id = :bvid"
print(await connection.fetchone(sql, [1]))
print()
print("2. Bind by position: multiple values")
sql = "select * from SampleQueryTab where id = :bvid and 123 = :otherbind"
print(await connection.fetchone(sql, [2, 123]))
print()
# With bind-by-position, the order of the data in the bind list matches
# the order of the placeholders used in the SQL statement. The bind
# list data order is not associated by the name of the bind variable
# placeholders in the SQL statement, even though those names are ":1"
# and ":2".
print(
"3. Bind by position: multiple values with numeric placeholder names"
)
sql = "select * from SampleQueryTab where id = :2 and 456 = :1"
print(await connection.fetchone(sql, [3, 456]))
print()
# With bind-by-position, repeated use of bind placeholder names in the
# SQL statement requires the input list data to be repeated.
print("4. Bind by position: multiple values with a repeated placeholder")
sql = "select * from SampleQueryTab where id = :2 and 3 = :2"
print(await connection.fetchall(sql, [3, 3]))
print()
# Bind by position with tuples
print("5. Bind by position with single value tuple")
sql = "select * from SampleQueryTab where id = :bvid"
print(await connection.fetchone(sql, (4,)))
print()
print("6. Bind by position with a multiple value tuple")
sql = "select * from SampleQueryTab where id = :bvid and 789 = :otherbind"
print(await connection.fetchone(sql, (4, 789)))
print()
# Bind by name with a dictionary
print("7. Bind by name with a dictionary")
sql = "select * from SampleQueryTab where id = :bvid"
print(await connection.fetchone(sql, {"bvid": 4}))
print()
# With bind-by-name, repeated use of bind placeholder names in the SQL
# statement lets you supply the data once.
print("8. Bind by name with multiple value dict and repeated placeholders")
sql = "select * from SampleQueryTab where id = :bvid and 4 = :bvid"
print(await connection.fetchone(sql, {"bvid": 4}))
print()
# Bind by name with parameters. The execute() parameter names match the
# bind variable placeholder names.
print("9. Bind by name using parameters")
sql = "select * from SampleQueryTab where id = :bvid"
print(await connection.fetchone(sql, dict(bvid=5)))
print()
print("10. Bind by name using multiple parameters")
sql = "select * from SampleQueryTab where id = :bvid and 101 = :otherbind"
print(await connection.fetchone(sql, dict(bvid=5, otherbind=101)))
print()
# With bind-by-name, repeated use of bind placeholder names in the SQL
# statement lets you supply the data once.
print("11. Bind by name: multiple values with repeated placeholder names")
sql = "select * from SampleQueryTab where id = :bvid and 6 = :bvid"
print(await connection.fetchone(sql, dict(bvid=6)))
print()
# Rexcuting a query with different data values
sql = "select * from SampleQueryTab where id = :bvid"
print("12. Query results with id = 7")
print(await connection.fetchone(sql, [4]))
print()
print("13. Rexcuted query results with id = 1")
print(await connection.fetchone(sql, [1]))
print()
asyncio.run(main())

View File

@ -1,5 +1,5 @@
# -----------------------------------------------------------------------------
# Copyright (c) 2019, 2023, Oracle and/or its affiliates.
#------------------------------------------------------------------------------
# Copyright (c) 2019, 2022, Oracle and/or its affiliates.
#
# Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved.
#
@ -25,14 +25,14 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# -----------------------------------------------------------------------------
#------------------------------------------------------------------------------
# -----------------------------------------------------------------------------
#------------------------------------------------------------------------------
# bulk_aq.py
#
# Demonstrates how to use bulk enqueuing and dequeuing of messages with
# advanced queuing. It makes use of a RAW queue created in the sample setup.
# -----------------------------------------------------------------------------
#------------------------------------------------------------------------------
import oracledb
import sample_env
@ -50,18 +50,16 @@ PAYLOAD_DATA = [
"The ninth message",
"The tenth message",
"The eleventh message",
"The twelfth and final message",
"The twelfth and final message"
]
# this script is currently only supported in python-oracledb thick mode
oracledb.init_oracle_client(lib_dir=sample_env.get_oracle_client())
# connect to database
connection = oracledb.connect(
user=sample_env.get_main_user(),
password=sample_env.get_main_password(),
dsn=sample_env.get_connect_string(),
)
connection = oracledb.connect(user=sample_env.get_main_user(),
password=sample_env.get_main_password(),
dsn=sample_env.get_connect_string())
# create a queue
with connection.cursor() as cursor:

View File

@ -1,5 +1,5 @@
# -----------------------------------------------------------------------------
# Copyright (c) 2019, 2023, Oracle and/or its affiliates.
#------------------------------------------------------------------------------
# Copyright (c) 2019, 2022, Oracle and/or its affiliates.
#
# This software is dual-licensed to you under the Universal Permissive License
# (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl and Apache License
@ -20,9 +20,9 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# -----------------------------------------------------------------------------
#------------------------------------------------------------------------------
# -----------------------------------------------------------------------------
#------------------------------------------------------------------------------
# call_timeout.py
#
# Demonstrates the use of the Oracle Client 18c feature that enables round
@ -30,7 +30,7 @@
# (in milliseconds) has passed without a response from the database.
#
# This script requires Oracle Client 18.1 and higher when using thick mode.
# -----------------------------------------------------------------------------
#------------------------------------------------------------------------------
import oracledb
import sample_env
@ -39,26 +39,23 @@ import sample_env
if not sample_env.get_is_thin():
oracledb.init_oracle_client(lib_dir=sample_env.get_oracle_client())
connection = oracledb.connect(
user=sample_env.get_main_user(),
password=sample_env.get_main_password(),
dsn=sample_env.get_connect_string(),
)
connection = oracledb.connect(user=sample_env.get_main_user(),
password=sample_env.get_main_password(),
dsn=sample_env.get_connect_string())
connection.call_timeout = 2000
print("Call timeout set at", connection.call_timeout, "milliseconds...")
with connection.cursor() as cursor:
cursor.execute("select sysdate from dual")
(today,) = cursor.fetchone()
today, = cursor.fetchone()
print("Fetch of current date before timeout:", today)
# dbms_session.sleep() replaces dbms_lock.sleep() from Oracle Database 18c
sleep_proc_name = (
"dbms_session.sleep"
if int(connection.version.split(".")[0]) >= 18
else "dbms_lock.sleep"
)
sleep_proc_name = "dbms_session.sleep" \
if int(connection.version.split(".")[0]) >= 18 \
else "dbms_lock.sleep"
print("Sleeping...should time out...")
try:
@ -67,5 +64,5 @@ with connection.cursor() as cursor:
print("ERROR:", e)
cursor.execute("select sysdate from dual")
(today,) = cursor.fetchone()
today, = cursor.fetchone()
print("Fetch of current date after timeout:", today)

View File

@ -1,73 +0,0 @@
# -----------------------------------------------------------------------------
# Copyright (c) 2023, Oracle and/or its affiliates.
#
# This software is dual-licensed to you under the Universal Permissive License
# (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl and Apache License
# 2.0 as shown at http://www.apache.org/licenses/LICENSE-2.0. You may choose
# either license.
#
# If you elect to accept the software under the Apache License, Version 2.0,
# the following applies:
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# -----------------------------------------------------------------------------
# -----------------------------------------------------------------------------
# call_timeout_async.py
#
# An asynchronous version of call_timeout.py
#
# Demonstrates the use of the feature that enables round trips to the database
# to time out if a specified amount of time (in milliseconds) has passed
# without a response from the database.
# -----------------------------------------------------------------------------
import asyncio
import oracledb
import sample_env
async def main():
connection = await oracledb.connect_async(
user=sample_env.get_main_user(),
password=sample_env.get_main_password(),
dsn=sample_env.get_connect_string(),
)
connection.call_timeout = 2000
print("Call timeout set at", connection.call_timeout, "milliseconds...")
with connection.cursor() as cursor:
(today,) = await connection.fetchone("select sysdate from dual")
print("Fetch of current date before timeout:", today)
# dbms_session.sleep() replaces dbms_lock.sleep() from Oracle Database
# 18c
sleep_proc_name = (
"dbms_session.sleep"
if int(connection.version.split(".")[0]) >= 18
else "dbms_lock.sleep"
)
print("Sleeping...should time out...")
try:
await cursor.callproc(sleep_proc_name, (3,))
except oracledb.DatabaseError as e:
print("ERROR:", e)
(today,) = await connection.fetchone("select sysdate from dual")
print("Fetch of current date after timeout:", today)
asyncio.run(main())

View File

@ -1,5 +1,5 @@
# -----------------------------------------------------------------------------
# Copyright (c) 2022, 2023, Oracle and/or its affiliates.
#------------------------------------------------------------------------------
# Copyright (c) 2022, Oracle and/or its affiliates.
#
# This software is dual-licensed to you under the Universal Permissive License
# (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl and Apache License
@ -20,9 +20,9 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# -----------------------------------------------------------------------------
#------------------------------------------------------------------------------
# -----------------------------------------------------------------------------
#------------------------------------------------------------------------------
# connection_pool.py
#
# Demonstrates the use of connection pooling using a Flask web application.
@ -56,7 +56,7 @@
# To insert new a user 'fred' you can call:
# http://127.0.0.1:8080/post/fred
#
# -----------------------------------------------------------------------------
#------------------------------------------------------------------------------
import os
import sys
@ -67,17 +67,17 @@ import oracledb
import sample_env
# Port to listen on
port = int(os.environ.get("PORT", "8080"))
port = int(os.environ.get('PORT', '8080'))
# determine whether to use python-oracledb thin mode or thick mode
if not sample_env.get_is_thin():
oracledb.init_oracle_client(lib_dir=sample_env.get_oracle_client())
# -----------------------------------------------------------------------------
#------------------------------------------------------------------------------
# start_pool(): starts the connection pool
def start_pool():
# Generally a fixed-size pool is recommended, i.e. pool_min=pool_max. Here
# the pool contains 4 connections, which will allow 4 concurrent users.
@ -85,19 +85,16 @@ def start_pool():
pool_max = 4
pool_inc = 0
pool = oracledb.create_pool(
user=sample_env.get_main_user(),
password=sample_env.get_main_password(),
dsn=sample_env.get_connect_string(),
min=pool_min,
max=pool_max,
increment=pool_inc,
session_callback=init_session,
)
pool = oracledb.create_pool(user=sample_env.get_main_user(),
password=sample_env.get_main_password(),
dsn=sample_env.get_connect_string(),
min=pool_min,
max=pool_max,
increment=pool_inc,
session_callback=init_session)
return pool
# init_session(): a 'session callback' to efficiently set any initial state
# that each connection should have.
#
@ -110,81 +107,63 @@ def start_pool():
#
def init_session(connection, requestedTag_ignored):
with connection.cursor() as cursor:
cursor.execute(
"""
cursor.execute("""
alter session set
time_zone = 'UTC'
nls_date_format = 'YYYY-MM-DD HH24:MI'
"""
)
# -----------------------------------------------------------------------------
time_zone = 'UTC'
nls_date_format = 'YYYY-MM-DD HH24:MI'""")
#------------------------------------------------------------------------------
# create_schema(): drop and create the demo table, and add a row
def create_schema():
with pool.acquire() as connection:
with connection.cursor() as cursor:
cursor.execute(
"""
cursor.execute("""
begin
begin
execute immediate 'drop table demo';
begin
execute immediate 'drop table demo';
exception when others then
if sqlcode <> -942 then
raise;
end if;
end;
if sqlcode <> -942 then
raise;
end if;
end;
execute immediate 'create table demo (
id number generated by default as identity,
username varchar2(40)
)';
execute immediate 'create table demo (
id number generated by default as identity,
username varchar2(40))';
execute immediate 'insert into demo (username) values
(''chris'')';
execute immediate 'insert into demo (username) values (''chris'')';
commit;
end;
"""
)
commit;
end;""")
# -----------------------------------------------------------------------------
#------------------------------------------------------------------------------
app = Flask(__name__)
# Display a welcome message on the 'home' page
@app.route("/")
@app.route('/')
def index():
return "Welcome to the demo app"
# Add a new username
#
# The new user's id is generated by the database and returned in the OUT bind
# variable 'idbv'.
@app.route("/post/<string:username>")
@app.route('/post/<string:username>')
def post(username):
with pool.acquire() as connection:
with connection.cursor() as cursor:
connection.autocommit = True
idbv = cursor.var(int)
cursor.execute(
"""
cursor.execute("""
insert into demo (username)
values (:unbv)
returning id into :idbv
""",
[username, idbv],
)
return f"Inserted {username} with id {idbv.getvalue()[0]}"
returning id into :idbv""", [username, idbv])
return f'Inserted {username} with id {idbv.getvalue()[0]}'
# Show the username for a given id
@app.route("/user/<int:id>")
@app.route('/user/<int:id>')
def show_username(id):
with pool.acquire() as connection:
with connection.cursor() as cursor:
@ -192,10 +171,10 @@ def show_username(id):
r = cursor.fetchone()
return r[0] if r is not None else "Unknown user id"
#------------------------------------------------------------------------------
# -----------------------------------------------------------------------------
if __name__ == '__main__':
if __name__ == "__main__":
# Start a pool of connections
pool = start_pool()
@ -203,7 +182,7 @@ if __name__ == "__main__":
create_schema()
m = f"\nTry loading http://127.0.0.1:{port}/user/1 in a browser\n"
sys.modules["flask.cli"].show_server_banner = lambda *x: print(m)
sys.modules['flask.cli'].show_server_banner = lambda *x: print(m)
# Start a webserver
app.run(port=port)

View File

@ -1,5 +1,5 @@
# -----------------------------------------------------------------------------
# Copyright (c) 2016, 2023, Oracle and/or its affiliates.
#------------------------------------------------------------------------------
# Copyright (c) 2016, 2022, Oracle and/or its affiliates.
#
# Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved.
#
@ -25,16 +25,16 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# -----------------------------------------------------------------------------
#------------------------------------------------------------------------------
# -----------------------------------------------------------------------------
#------------------------------------------------------------------------------
# cqn.py
#
# Demonstrates using continuous query notification in Python, a feature that is
# available in Oracle 11g and later. Once this script is running, use another
# session to insert, update or delete rows from the table TestTempTable and you
# will see the notification of that change.
# -----------------------------------------------------------------------------
#------------------------------------------------------------------------------
import time
@ -46,7 +46,6 @@ oracledb.init_oracle_client(lib_dir=sample_env.get_oracle_client())
registered = True
def callback(message):
global registered
print("Message type:", message.type)
@ -71,18 +70,14 @@ def callback(message):
print("-" * 60)
print("=" * 60)
connection = oracledb.connect(
user=sample_env.get_main_user(),
password=sample_env.get_main_password(),
dsn=sample_env.get_connect_string(),
events=True,
)
connection = oracledb.connect(user=sample_env.get_main_user(),
password=sample_env.get_main_password(),
dsn=sample_env.get_connect_string(),
events=True)
qos = oracledb.SUBSCR_QOS_QUERY | oracledb.SUBSCR_QOS_ROWIDS
sub = connection.subscribe(
callback=callback, timeout=1800, qos=qos, client_initiated=True
)
sub = connection.subscribe(callback=callback, timeout=1800,
qos=qos, client_initiated=True)
print("Subscription:", sub)
print("--> Connection:", sub.connection)
print("--> Callback:", sub.callback)

View File

@ -1,5 +1,5 @@
# -----------------------------------------------------------------------------
# Copyright (c) 2020, 2023, Oracle and/or its affiliates.
#------------------------------------------------------------------------------
# Copyright (c) 2020, 2022, Oracle and/or its affiliates.
#
# This software is dual-licensed to you under the Universal Permissive License
# (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl and Apache License
@ -20,9 +20,9 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# -----------------------------------------------------------------------------
#------------------------------------------------------------------------------
# -----------------------------------------------------------------------------
#------------------------------------------------------------------------------
# cqn_pool.py
#
# Demonstrates using continuous query notification in Python, a feature that is
@ -33,7 +33,7 @@
# This script differs from cqn.py in that it shows how a connection can be
# acquired from a session pool and used to query the changes that have been
# made.
# -----------------------------------------------------------------------------
#------------------------------------------------------------------------------
import time
@ -45,7 +45,6 @@ oracledb.init_oracle_client(lib_dir=sample_env.get_oracle_client())
registered = True
def callback(message):
global registered
if not message.registered:
@ -70,36 +69,26 @@ def callback(message):
if row.operation & oracledb.OPCODE_UPDATE:
ops.append("updated")
cursor = connection.cursor()
cursor.execute(
"""
select IntCol
from TestTempTable
where rowid = :rid
""",
rid=row.rowid,
)
(int_col,) = cursor.fetchone()
cursor.execute("""
select IntCol
from TestTempTable
where rowid = :rid""",
rid=row.rowid)
int_col, = cursor.fetchone()
print(" Row with IntCol", int_col, "was", " and ".join(ops))
if num_rows_deleted > 0:
print(" ", num_rows_deleted, "rows deleted")
print("=" * 60)
pool = oracledb.create_pool(
user=sample_env.get_main_user(),
password=sample_env.get_main_password(),
dsn=sample_env.get_connect_string(),
min=1,
max=4,
increment=1,
events=True,
)
pool = oracledb.create_pool(user=sample_env.get_main_user(),
password=sample_env.get_main_password(),
dsn=sample_env.get_connect_string(),
min=1, max=4, increment=1, events=True)
with pool.acquire() as connection:
qos = oracledb.SUBSCR_QOS_QUERY | oracledb.SUBSCR_QOS_ROWIDS
sub = connection.subscribe(
callback=callback, timeout=1800, qos=qos, client_initiated=True
)
sub = connection.subscribe(callback=callback, timeout=1800,
qos=qos, client_initiated=True)
print("Subscription created with ID:", sub.id)
query_id = sub.registerquery("select * from TestTempTable")
print("Registered query with ID:", query_id)

View File

@ -1,5 +1,5 @@
# -----------------------------------------------------------------------------
# Copyright (c) 2020, 2023, Oracle and/or its affiliates.
#------------------------------------------------------------------------------
# Copyright (c) 2020, 2022, Oracle and/or its affiliates.
#
# This software is dual-licensed to you under the Universal Permissive License
# (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl and Apache License
@ -20,38 +20,33 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# -----------------------------------------------------------------------------
#------------------------------------------------------------------------------
# -----------------------------------------------------------------------------
#------------------------------------------------------------------------------
# create_schema.py
#
# Creates users and populates their schemas with the tables and packages
# necessary for running the python-oracledb sample scripts. An edition is also
# created for the demonstration of PL/SQL editioning.
# -----------------------------------------------------------------------------
#------------------------------------------------------------------------------
import oracledb
import drop_schema
import sample_env
# connect as administrative user (usually SYSTEM or ADMIN)
conn = sample_env.get_admin_connection()
conn = oracledb.connect(sample_env.get_admin_connect_string())
# drop existing users and editions, if applicable
drop_schema.drop_schema(conn)
# create sample schema and edition
print("Creating sample schemas and edition...")
sample_env.run_sql_script(
conn,
"create_schema",
main_user=sample_env.get_main_user(),
main_password=sample_env.get_main_password(),
edition_user=sample_env.get_edition_user(),
edition_password=sample_env.get_edition_password(),
edition_name=sample_env.get_edition_name(),
)
if sample_env.get_server_version() >= (21, 0):
sample_env.run_sql_script(
conn, "create_schema_21", main_user=sample_env.get_main_user()
)
sample_env.run_sql_script(conn, "create_schema",
main_user=sample_env.get_main_user(),
main_password=sample_env.get_main_password(),
edition_user=sample_env.get_edition_user(),
edition_password=sample_env.get_edition_password(),
edition_name=sample_env.get_edition_name())
print("Done.")

View File

@ -1,5 +1,5 @@
# -----------------------------------------------------------------------------
# Copyright (c) 2016, 2023, Oracle and/or its affiliates.
#------------------------------------------------------------------------------
# Copyright (c) 2016, 2022, Oracle and/or its affiliates.
#
# Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved.
#
@ -25,16 +25,16 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# -----------------------------------------------------------------------------
#------------------------------------------------------------------------------
# -----------------------------------------------------------------------------
#------------------------------------------------------------------------------
# database_change_notification.py
#
# Demonstrates using database change notification in Python, a feature that is
# available in Oracle 10g Release 2. Once this script is running, use another
# session to insert, update or delete rows from the table TestTempTable and you
# will see the notification of that change.
# -----------------------------------------------------------------------------
#------------------------------------------------------------------------------
import time
@ -46,7 +46,6 @@ oracledb.init_oracle_client(lib_dir=sample_env.get_oracle_client())
registered = True
def callback(message):
global registered
print("Message type:", message.type)
@ -68,20 +67,13 @@ def callback(message):
print("-" * 60)
print("=" * 60)
connection = oracledb.connect(user=sample_env.get_main_user(),
password=sample_env.get_main_password(),
dsn=sample_env.get_connect_string(),
events=True)
connection = oracledb.connect(
user=sample_env.get_main_user(),
password=sample_env.get_main_password(),
dsn=sample_env.get_connect_string(),
events=True,
)
sub = connection.subscribe(
callback=callback,
timeout=1800,
qos=oracledb.SUBSCR_QOS_ROWIDS,
client_initiated=True,
)
sub = connection.subscribe(callback=callback, timeout=1800,
qos=oracledb.SUBSCR_QOS_ROWIDS, client_initiated=True)
print("Subscription:", sub)
print("--> Connection:", sub.connection)
print("--> ID:", sub.id)

View File

@ -1,5 +1,5 @@
# -----------------------------------------------------------------------------
# Copyright (c) 2016, 2023, Oracle and/or its affiliates.
#------------------------------------------------------------------------------
# Copyright (c) 2016, 2022, Oracle and/or its affiliates.
#
# Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved.
#
@ -25,14 +25,14 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# -----------------------------------------------------------------------------
#------------------------------------------------------------------------------
# -----------------------------------------------------------------------------
#------------------------------------------------------------------------------
# database_shutdown.py
#
# Demonstrates shutting down a database using Python. The connection used
# assumes that the environment variable ORACLE_SID has been set.
# -----------------------------------------------------------------------------
#------------------------------------------------------------------------------
import oracledb
import sample_env

View File

@ -1,5 +1,5 @@
# -----------------------------------------------------------------------------
# Copyright (c) 2016, 2023, Oracle and/or its affiliates.
#------------------------------------------------------------------------------
# Copyright (c) 2016, 2022, Oracle and/or its affiliates.
#
# Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved.
#
@ -25,14 +25,14 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# -----------------------------------------------------------------------------
#------------------------------------------------------------------------------
# -----------------------------------------------------------------------------
#------------------------------------------------------------------------------
# database_startup.py
#
# Demonstrates starting up a database using Python. The connection used
# assumes that the environment variable ORACLE_SID has been set.
# -----------------------------------------------------------------------------
#------------------------------------------------------------------------------
import oracledb
import sample_env

View File

@ -1,5 +1,5 @@
# -----------------------------------------------------------------------------
# Copyright (c) 2020, 2023, Oracle and/or its affiliates.
#------------------------------------------------------------------------------
# Copyright (c) 2020, 2022, Oracle and/or its affiliates.
#
# This software is dual-licensed to you under the Universal Permissive License
# (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl and Apache License
@ -20,14 +20,14 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# -----------------------------------------------------------------------------
#------------------------------------------------------------------------------
# -----------------------------------------------------------------------------
#------------------------------------------------------------------------------
# dbms_output.py
#
# Demonstrates one method of fetching the lines produced by the DBMS_OUTPUT
# package.
# -----------------------------------------------------------------------------
#------------------------------------------------------------------------------
import oracledb
import sample_env
@ -36,26 +36,22 @@ import sample_env
if not sample_env.get_is_thin():
oracledb.init_oracle_client(lib_dir=sample_env.get_oracle_client())
connection = oracledb.connect(
user=sample_env.get_main_user(),
password=sample_env.get_main_password(),
dsn=sample_env.get_connect_string(),
)
connection = oracledb.connect(user=sample_env.get_main_user(),
password=sample_env.get_main_password(),
dsn=sample_env.get_connect_string())
with connection.cursor() as cursor:
# enable DBMS_OUTPUT
cursor.callproc("dbms_output.enable")
# execute some PL/SQL that generates output with DBMS_OUTPUT.PUT_LINE
cursor.execute(
"""
begin
dbms_output.put_line('This is some text');
dbms_output.put_line('');
dbms_output.put_line('Demonstrating use of DBMS_OUTPUT');
end;
"""
)
cursor.execute("""
begin
dbms_output.put_line('This is some text');
dbms_output.put_line('');
dbms_output.put_line('Demonstrating use of DBMS_OUTPUT');
end;""")
# tune this size for your application
chunk_size = 10

View File

@ -1,83 +0,0 @@
# -----------------------------------------------------------------------------
# Copyright (c) 2023, Oracle and/or its affiliates.
#
# This software is dual-licensed to you under the Universal Permissive License
# (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl and Apache License
# 2.0 as shown at http://www.apache.org/licenses/LICENSE-2.0. You may choose
# either license.
#
# If you elect to accept the software under the Apache License, Version 2.0,
# the following applies:
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# -----------------------------------------------------------------------------
# -----------------------------------------------------------------------------
# dbms_output_async.py
#
# An asynchronous version of dbms_output.py
#
# Demonstrates one method of fetching the lines produced by the DBMS_OUTPUT
# package.
# -----------------------------------------------------------------------------
import asyncio
import oracledb
import sample_env
async def main():
connection = await oracledb.connect_async(
user=sample_env.get_main_user(),
password=sample_env.get_main_password(),
dsn=sample_env.get_connect_string(),
)
with connection.cursor() as cursor:
# enable DBMS_OUTPUT
await cursor.callproc("dbms_output.enable")
# execute some PL/SQL that generates output with DBMS_OUTPUT.PUT_LINE
await cursor.execute(
"""
begin
dbms_output.put_line('This is some text');
dbms_output.put_line('');
dbms_output.put_line('Demonstrating use of DBMS_OUTPUT');
end;
"""
)
# tune this size for your application
chunk_size = 10
# create variables to hold the output
lines_var = cursor.arrayvar(str, chunk_size)
num_lines_var = cursor.var(int)
num_lines_var.setvalue(0, chunk_size)
# fetch the text that was added by PL/SQL
while True:
await cursor.callproc(
"dbms_output.get_lines", (lines_var, num_lines_var)
)
num_lines = num_lines_var.getvalue()
lines = lines_var.getvalue()[:num_lines]
for line in lines:
print(line or "")
if num_lines < chunk_size:
break
asyncio.run(main())

View File

@ -1,5 +1,5 @@
# -----------------------------------------------------------------------------
# Copyright (c) 2017, 2023, Oracle and/or its affiliates.
#------------------------------------------------------------------------------
# Copyright (c) 2017, 2022, Oracle and/or its affiliates.
#
# Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved.
#
@ -25,14 +25,14 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# -----------------------------------------------------------------------------
#------------------------------------------------------------------------------
# -----------------------------------------------------------------------------
#------------------------------------------------------------------------------
# dml_returning_multiple_rows.py
#
# Demonstrates the use of DML returning with multiple rows being returned at
# once.
# -----------------------------------------------------------------------------
#------------------------------------------------------------------------------
import oracledb
import sample_env
@ -41,13 +41,12 @@ import sample_env
if not sample_env.get_is_thin():
oracledb.init_oracle_client(lib_dir=sample_env.get_oracle_client())
connection = oracledb.connect(
user=sample_env.get_main_user(),
password=sample_env.get_main_password(),
dsn=sample_env.get_connect_string(),
)
connection = oracledb.connect(user=sample_env.get_main_user(),
password=sample_env.get_main_password(),
dsn=sample_env.get_connect_string())
with connection.cursor() as cursor:
# truncate table first so that script can be rerun
print("Truncating table...")
cursor.execute("truncate table TestTempTable")
@ -63,14 +62,11 @@ with connection.cursor() as cursor:
int_col = cursor.var(int)
string_col = cursor.var(str)
print("Deleting data with DML returning...")
cursor.execute(
"""
delete from TestTempTable
returning IntCol, StringCol into :int_col, :string_col
""",
int_col=int_col,
string_col=string_col,
)
cursor.execute("""
delete from TestTempTable
returning IntCol, StringCol into :int_col, :string_col""",
int_col=int_col,
string_col=string_col)
print("Data returned:")
for int_val, string_val in zip(int_col.getvalue(), string_col.getvalue()):
print(tuple([int_val, string_val]))

View File

@ -1,80 +0,0 @@
# -----------------------------------------------------------------------------
# Copyright (c) 2023, Oracle and/or its affiliates.
#
# This software is dual-licensed to you under the Universal Permissive License
# (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl and Apache License
# 2.0 as shown at http://www.apache.org/licenses/LICENSE-2.0. You may choose
# either license.
#
# If you elect to accept the software under the Apache License, Version 2.0,
# the following applies:
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# -----------------------------------------------------------------------------
# -----------------------------------------------------------------------------
# dml_returning_multiple_rows_async.py
#
# An asynchronous version of dml_returning_multiple_rows.py
#
# Demonstrates the use of DML returning with multiple rows being returned at
# once.
# -----------------------------------------------------------------------------
import asyncio
import oracledb
import sample_env
async def main():
connection = await oracledb.connect_async(
user=sample_env.get_main_user(),
password=sample_env.get_main_password(),
dsn=sample_env.get_connect_string(),
)
with connection.cursor() as cursor:
# truncate table first so that script can be rerun
print("Truncating table...")
await cursor.execute("truncate table TestTempTable")
# populate table with a few rows
for i in range(5):
data = (i + 1, "Test String #%d" % (i + 1))
print("Adding row", data)
await cursor.execute(
"insert into TestTempTable values (:1, :2)", data
)
# now delete them and use DML returning to return the data that was
# deleted
int_col = cursor.var(int)
string_col = cursor.var(str)
print("Deleting data with DML returning...")
await cursor.execute(
"""
delete from TestTempTable
returning IntCol, StringCol into :int_col, :string_col
""",
int_col=int_col,
string_col=string_col,
)
print("Data returned:")
for int_val, string_val in zip(
int_col.getvalue(), string_col.getvalue()
):
print(tuple([int_val, string_val]))
asyncio.run(main())

View File

@ -1,5 +1,5 @@
# -----------------------------------------------------------------------------
# Copyright (c) 2022, 2023, Oracle and/or its affiliates.
#------------------------------------------------------------------------------
# Copyright (c) 2022, Oracle and/or its affiliates.
#
# This software is dual-licensed to you under the Universal Permissive License
# (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl and Apache License
@ -20,9 +20,9 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# -----------------------------------------------------------------------------
#------------------------------------------------------------------------------
# -----------------------------------------------------------------------------
#------------------------------------------------------------------------------
# drcp_pool.py
#
# Demonstrates the use of Database Resident Connection Pooling (DRCP)
@ -93,18 +93,18 @@
# Then you can query the data dictionary:
#
# select cclass_name, num_requests, num_hits,
# num_misses, num_waits, num_authentications as num_auths
# num_misses, num_waits, num_authentications
# from v$cpool_cc_stats;
#
# Output will be like:
#
# CCLASS_NAME NUM_REQUESTS NUM_HITS NUM_MISSES NUM_WAITS NUM_AUTHS
# ---------------- ------------ -------- ---------- --------- ---------
# PYTHONDEMO.MYAPP 1001 997 4 0 4
# CCLASS_NAME NUM_REQUESTS NUM_HITS NUM_MISSES NUM_WAITS NUM_AUTHENTICATIONS
# ---------------- ------------ -------- ---------- --------- -------------------
# PYTHONDEMO.MYAPP 1001 997 4 0 4
#
# With ADB-S databases, query V$CPOOL_CONN_INFO instead.
#
# -----------------------------------------------------------------------------
#------------------------------------------------------------------------------
import os
import sys
@ -115,17 +115,17 @@ import oracledb
import sample_env
# Port to listen on
port = int(os.environ.get("PORT", "8080"))
port = int(os.environ.get('PORT', '8080'))
# determine whether to use python-oracledb thin mode or thick mode
if not sample_env.get_is_thin():
oracledb.init_oracle_client(lib_dir=sample_env.get_oracle_client())
# -----------------------------------------------------------------------------
#------------------------------------------------------------------------------
# start_pool(): starts the connection pool
def start_pool():
# Generally a fixed-size pool is recommended, i.e. pool_min=pool_max. Here
# the pool contains 4 connections, which will allow 4 concurrent users.
@ -133,21 +133,16 @@ def start_pool():
pool_max = 4
pool_inc = 0
pool = oracledb.create_pool(
user=sample_env.get_main_user(),
password=sample_env.get_main_password(),
dsn=sample_env.get_drcp_connect_string(),
min=pool_min,
max=pool_max,
increment=pool_inc,
session_callback=init_session,
cclass="MYAPP",
purity=oracledb.ATTR_PURITY_SELF,
)
pool = oracledb.create_pool(user=sample_env.get_main_user(),
password=sample_env.get_main_password(),
dsn=sample_env.get_drcp_connect_string(),
min=pool_min, max=pool_max, increment=pool_inc,
session_callback=init_session,
cclass="MYAPP",
purity=oracledb.ATTR_PURITY_SELF)
return pool
# init_session(): a 'session callback' to efficiently set any initial state
# that each connection should have.
#
@ -160,81 +155,63 @@ def start_pool():
#
def init_session(connection, requestedTag_ignored):
with connection.cursor() as cursor:
cursor.execute(
"""
cursor.execute("""
alter session set
time_zone = 'UTC'
nls_date_format = 'YYYY-MM-DD HH24:MI'
"""
)
# -----------------------------------------------------------------------------
time_zone = 'UTC'
nls_date_format = 'YYYY-MM-DD HH24:MI'""")
#------------------------------------------------------------------------------
# create_schema(): drop and create the demo table, and add a row
def create_schema():
with pool.acquire() as connection:
with connection.cursor() as cursor:
cursor.execute(
"""
cursor.execute("""
begin
begin
execute immediate 'drop table demo';
begin
execute immediate 'drop table demo';
exception when others then
if sqlcode <> -942 then
raise;
end if;
end;
if sqlcode <> -942 then
raise;
end if;
end;
execute immediate 'create table demo (
id number generated by default as identity,
username varchar2(40)
)';
execute immediate 'create table demo (
id number generated by default as identity,
username varchar2(40))';
execute immediate 'insert into demo (username)
values (''chris'')';
execute immediate 'insert into demo (username) values (''chris'')';
commit;
end;
"""
)
commit;
end;""")
# -----------------------------------------------------------------------------
#------------------------------------------------------------------------------
app = Flask(__name__)
# Display a welcome message on the 'home' page
@app.route("/")
@app.route('/')
def index():
return "Welcome to the demo app"
# Add a new username
#
# The new user's id is generated by the database and returned in the OUT bind
# variable 'idbv'.
@app.route("/post/<string:username>")
@app.route('/post/<string:username>')
def post(username):
with pool.acquire() as connection:
with connection.cursor() as cursor:
connection.autocommit = True
idbv = cursor.var(int)
cursor.execute(
"""
cursor.execute("""
insert into demo (username)
values (:unbv)
returning id into :idbv
""",
[username, idbv],
)
return f"Inserted {username} with id {idbv.getvalue()[0]}"
returning id into :idbv""", [username, idbv])
return f'Inserted {username} with id {idbv.getvalue()[0]}'
# Show the username for a given id
@app.route("/user/<int:id>")
@app.route('/user/<int:id>')
def show_username(id):
with pool.acquire() as connection:
with connection.cursor() as cursor:
@ -242,10 +219,10 @@ def show_username(id):
r = cursor.fetchone()
return r[0] if r is not None else "Unknown user id"
#------------------------------------------------------------------------------
# -----------------------------------------------------------------------------
if __name__ == '__main__':
if __name__ == "__main__":
# Start a pool of connections
pool = start_pool()
@ -253,7 +230,7 @@ if __name__ == "__main__":
create_schema()
m = f"\nTry loading http://127.0.0.1:{port}/user/1 in a browser\n"
sys.modules["flask.cli"].show_server_banner = lambda *x: print(m)
sys.modules['flask.cli'].show_server_banner = lambda *x: print(m)
# Start a webserver
app.run(port=port)

View File

@ -1,5 +1,5 @@
# -----------------------------------------------------------------------------
# Copyright (c) 2020, 2023, Oracle and/or its affiliates.
#------------------------------------------------------------------------------
# Copyright (c) 2020, 2022, Oracle and/or its affiliates.
#
# This software is dual-licensed to you under the Universal Permissive License
# (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl and Apache License
@ -20,9 +20,9 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# -----------------------------------------------------------------------------
#------------------------------------------------------------------------------
# -----------------------------------------------------------------------------
#------------------------------------------------------------------------------
# drop_schema.py
#
# Drops the database objects used by the python-oracledb samples.
@ -30,23 +30,19 @@
# This script is also executed by the Python script sample_setup.py for
# dropping the existing users and editions, if applicable, before creating the
# sample schemas and editions.
# -----------------------------------------------------------------------------
#------------------------------------------------------------------------------
import oracledb
import sample_env
def drop_schema(conn):
print("Dropping sample schemas and edition...")
sample_env.run_sql_script(
conn,
"drop_schema",
main_user=sample_env.get_main_user(),
edition_user=sample_env.get_edition_user(),
edition_name=sample_env.get_edition_name(),
)
sample_env.run_sql_script(conn, "drop_schema",
main_user=sample_env.get_main_user(),
edition_user=sample_env.get_edition_user(),
edition_name=sample_env.get_edition_name())
if __name__ == "__main__":
conn = sample_env.get_admin_connection()
conn = oracledb.connect(sample_env.get_admin_connect_string())
drop_schema(conn)
print("Done.")

View File

@ -1,5 +1,5 @@
# -----------------------------------------------------------------------------
# Copyright (c) 2016, 2023, Oracle and/or its affiliates.
#------------------------------------------------------------------------------
# Copyright (c) 2016, 2022, Oracle and/or its affiliates.
#
# Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved.
#
@ -25,16 +25,16 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# -----------------------------------------------------------------------------
#------------------------------------------------------------------------------
# -----------------------------------------------------------------------------
#------------------------------------------------------------------------------
# editioning.py
#
# Demonstrates the use of Edition-Based Redefinition, a feature that is
# available in Oracle Database 11.2 and higher. See the Oracle documentation on
# the subject for additional information. Adjust the contents at the top of the
# script for your own database as needed.
# -----------------------------------------------------------------------------
#------------------------------------------------------------------------------
import os
@ -50,72 +50,49 @@ edition_name = sample_env.get_edition_name()
connection = oracledb.connect(edition_connect_string)
print("Edition should be None, actual value is:", repr(connection.edition))
cursor = connection.cursor()
cursor.execute(
"""
create or replace function TestEditions return varchar2 as
begin
return 'Base Procedure';
end;
"""
)
cursor.execute("""
create or replace function TestEditions return varchar2 as
begin
return 'Base Procedure';
end;""")
result = cursor.callfunc("TestEditions", str)
print(
"Function should return 'Base Procedure', actually returns:", repr(result)
)
print("Function should return 'Base Procedure', actually returns:",
repr(result))
# next, change the edition and recreate the procedure in the new edition
cursor.execute("alter session set edition = %s" % edition_name)
print(
"Edition should be",
repr(edition_name.upper()),
"actual value is:",
repr(connection.edition),
)
cursor.execute(
"""
create or replace function TestEditions return varchar2 as
begin
return 'Edition 1 Procedure';
end;
"""
)
print("Edition should be", repr(edition_name.upper()),
"actual value is:", repr(connection.edition))
cursor.execute("""
create or replace function TestEditions return varchar2 as
begin
return 'Edition 1 Procedure';
end;""")
result = cursor.callfunc("TestEditions", str)
print(
"Function should return 'Edition 1 Procedure', actually returns:",
repr(result),
)
print("Function should return 'Edition 1 Procedure', actually returns:",
repr(result))
# next, change the edition back to the base edition and demonstrate that the
# original function is being called
cursor.execute("alter session set edition = ORA$BASE")
result = cursor.callfunc("TestEditions", str)
print(
"Function should return 'Base Procedure', actually returns:", repr(result)
)
print("Function should return 'Base Procedure', actually returns:",
repr(result))
# the edition can be set upon connection
connection = oracledb.connect(
edition_connect_string, edition=edition_name.upper()
)
connection = oracledb.connect(edition_connect_string,
edition=edition_name.upper())
cursor = connection.cursor()
result = cursor.callfunc("TestEditions", str)
print(
"Function should return 'Edition 1 Procedure', actually returns:",
repr(result),
)
print("Function should return 'Edition 1 Procedure', actually returns:",
repr(result))
# it can also be set via the environment variable ORA_EDITION
os.environ["ORA_EDITION"] = edition_name.upper()
connection = oracledb.connect(edition_connect_string)
print(
"Edition should be",
repr(edition_name.upper()),
"actual value is:",
repr(connection.edition),
)
print("Edition should be", repr(edition_name.upper()),
"actual value is:", repr(connection.edition))
cursor = connection.cursor()
result = cursor.callfunc("TestEditions", str)
print(
"Function should return 'Edition 1 Procedure', actually returns:",
repr(result),
)
print("Function should return 'Edition 1 Procedure', actually returns:",
repr(result))

View File

@ -1,5 +1,5 @@
# -----------------------------------------------------------------------------
# Copyright (c) 2016, 2023, Oracle and/or its affiliates.
#------------------------------------------------------------------------------
# Copyright (c) 2016, 2022, Oracle and/or its affiliates.
#
# This software is dual-licensed to you under the Universal Permissive License
# (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl and Apache License
@ -20,14 +20,14 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# -----------------------------------------------------------------------------
#------------------------------------------------------------------------------
# -----------------------------------------------------------------------------
#------------------------------------------------------------------------------
# generic_row_factory.py
#
# Demonstrates the ability to return named tuples for all queries using a
# subclassed cursor and row factory.
# -----------------------------------------------------------------------------
#------------------------------------------------------------------------------
import collections
@ -38,32 +38,32 @@ import sample_env
if not sample_env.get_is_thin():
oracledb.init_oracle_client(lib_dir=sample_env.get_oracle_client())
class Connection(oracledb.Connection):
def cursor(self):
return Cursor(self)
class Cursor(oracledb.Cursor):
def execute(self, statement, args=None):
prepare_needed = self.statement != statement
prepare_needed = (self.statement != statement)
result = super().execute(statement, args or [])
if prepare_needed:
description = self.description
if description is not None:
names = [d.name for d in description]
names = [d[0] for d in description]
self.rowfactory = collections.namedtuple("GenericQuery", names)
return result
# create a new subclassed connection and cursor
connection = Connection(
user=sample_env.get_main_user(),
password=sample_env.get_main_password(),
dsn=sample_env.get_connect_string(),
)
connection = Connection(user=sample_env.get_main_user(),
password=sample_env.get_main_password(),
dsn=sample_env.get_connect_string())
with connection.cursor() as cursor:
# the names are now available directly for each query executed
for row in cursor.execute("select ParentId, Description from ParentTable"):
print(row.PARENTID, "->", row.DESCRIPTION)

View File

@ -1,80 +0,0 @@
# -----------------------------------------------------------------------------
# Copyright (c) 2023, Oracle and/or its affiliates.
#
# This software is dual-licensed to you under the Universal Permissive License
# (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl and Apache License
# 2.0 as shown at http://www.apache.org/licenses/LICENSE-2.0. You may choose
# either license.
#
# If you elect to accept the software under the Apache License, Version 2.0,
# the following applies:
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# -----------------------------------------------------------------------------
# -----------------------------------------------------------------------------
# generic_row_factory_async.py
#
# An asynchronous version of generic_row_factory.py
#
# Demonstrates the ability to return named tuples for all queries using a
# subclassed cursor and row factory.
# -----------------------------------------------------------------------------
import asyncio
import collections
import oracledb
import sample_env
class Connection(oracledb.AsyncConnection):
def cursor(self):
return Cursor(self)
class Cursor(oracledb.AsyncCursor):
async def execute(self, statement, args=None):
prepare_needed = self.statement != statement
result = await super().execute(statement, args or [])
if prepare_needed:
description = self.description
if description is not None:
names = [d.name for d in description]
self.rowfactory = collections.namedtuple("GenericQuery", names)
return result
async def main():
# create a new subclassed connection and cursor
connection = await oracledb.connect_async(
conn_class=Connection,
user=sample_env.get_main_user(),
password=sample_env.get_main_password(),
dsn=sample_env.get_connect_string(),
)
with connection.cursor() as cursor:
# the names are now available directly for each query executed
await cursor.execute("select ParentId, Description from ParentTable")
async for row in cursor:
print(row.PARENTID, "->", row.DESCRIPTION)
print()
await cursor.execute("select ChildId, Description from ChildTable")
async for row in cursor:
print(row.CHILDID, "->", row.DESCRIPTION)
print()
asyncio.run(main())

View File

@ -1,5 +1,5 @@
# -----------------------------------------------------------------------------
# Copyright (c) 2016, 2023, Oracle and/or its affiliates.
#------------------------------------------------------------------------------
# Copyright (c) 2016, 2022, Oracle and/or its affiliates.
#
# Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved.
#
@ -25,15 +25,15 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# -----------------------------------------------------------------------------
#------------------------------------------------------------------------------
# -----------------------------------------------------------------------------
#------------------------------------------------------------------------------
# implicit_results.py
#
# Demonstrates the use of the Oracle Database 12.1 feature that allows PL/SQL
# procedures to return result sets implicitly, without having to explicitly
# define them.
# -----------------------------------------------------------------------------
#------------------------------------------------------------------------------
import oracledb
import sample_env
@ -42,34 +42,30 @@ import sample_env
if not sample_env.get_is_thin():
oracledb.init_oracle_client(lib_dir=sample_env.get_oracle_client())
connection = oracledb.connect(
user=sample_env.get_main_user(),
password=sample_env.get_main_password(),
dsn=sample_env.get_connect_string(),
)
connection = oracledb.connect(user=sample_env.get_main_user(),
password=sample_env.get_main_password(),
dsn=sample_env.get_connect_string())
with connection.cursor() as cursor:
# A PL/SQL block that returns two cursors
cursor.execute(
"""
declare
c1 sys_refcursor;
c2 sys_refcursor;
begin
cursor.execute("""
declare
c1 sys_refcursor;
c2 sys_refcursor;
begin
open c1 for
select * from TestNumbers;
open c1 for
select * from TestNumbers;
dbms_sql.return_result(c1);
dbms_sql.return_result(c1);
open c2 for
select * from TestStrings;
open c2 for
select * from TestStrings;
dbms_sql.return_result(c2);
dbms_sql.return_result(c2);
end;
"""
)
end;""")
# display results
for ix, result_set in enumerate(cursor.getimplicitresults()):

View File

@ -1,79 +0,0 @@
# -----------------------------------------------------------------------------
# Copyright (c) 2023, Oracle and/or its affiliates.
#
# This software is dual-licensed to you under the Universal Permissive License
# (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl and Apache License
# 2.0 as shown at http://www.apache.org/licenses/LICENSE-2.0. You may choose
# either license.
#
# If you elect to accept the software under the Apache License, Version 2.0,
# the following applies:
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# -----------------------------------------------------------------------------
# -----------------------------------------------------------------------------
# implicit_results_async.py
#
# An asynchronous version of implicit_results.py
#
# Demonstrates the use of the Oracle Database 12.1 feature that allows PL/SQL
# procedures to return result sets implicitly, without having to explicitly
# define them.
# -----------------------------------------------------------------------------
import asyncio
import oracledb
import sample_env
async def main():
connection = await oracledb.connect_async(
user=sample_env.get_main_user(),
password=sample_env.get_main_password(),
dsn=sample_env.get_connect_string(),
)
with connection.cursor() as cursor:
# A PL/SQL block that returns two cursors
await cursor.execute(
"""
declare
c1 sys_refcursor;
c2 sys_refcursor;
begin
open c1 for
select * from TestNumbers;
dbms_sql.return_result(c1);
open c2 for
select * from TestStrings;
dbms_sql.return_result(c2);
end;
"""
)
# display results
for ix, result_set in enumerate(cursor.getimplicitresults()):
print("Result Set #" + str(ix + 1))
async for row in result_set:
print(row)
print()
asyncio.run(main())

View File

@ -1,5 +1,5 @@
# -----------------------------------------------------------------------------
# Copyright (c) 2016, 2023, Oracle and/or its affiliates.
#------------------------------------------------------------------------------
# Copyright (c) 2016, 2022, Oracle and/or its affiliates.
#
# Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved.
#
@ -25,14 +25,14 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# -----------------------------------------------------------------------------
#------------------------------------------------------------------------------
# -----------------------------------------------------------------------------
#------------------------------------------------------------------------------
# insert_geometry.py
#
# Demonstrates the ability to create Oracle objects (this example uses
# SDO_GEOMETRY) and insert them into a table.
# -----------------------------------------------------------------------------
#------------------------------------------------------------------------------
import oracledb
import sample_env
@ -41,11 +41,9 @@ import sample_env
if not sample_env.get_is_thin():
oracledb.init_oracle_client(lib_dir=sample_env.get_oracle_client())
connection = oracledb.connect(
user=sample_env.get_main_user(),
password=sample_env.get_main_password(),
dsn=sample_env.get_connect_string(),
)
connection = oracledb.connect(user=sample_env.get_main_user(),
password=sample_env.get_main_password(),
dsn=sample_env.get_connect_string())
# create and populate Oracle objects
type_obj = connection.gettype("MDSYS.SDO_GEOMETRY")
@ -60,7 +58,25 @@ obj.SDO_ORDINATES.extend([1, 1, 5, 7])
print("Created object", obj)
with connection.cursor() as cursor:
cursor.execute("truncate table TestGeometry")
# create sample table
cursor.execute("""
begin
begin
execute immediate 'drop table TestGeometry';
exception
when others then
if sqlcode <> -942 then
raise;
end if;
end;
execute immediate 'create table TestGeometry (
IntCol number(9) not null,
Geometry MDSYS.SDO_GEOMETRY)';
end;""")
print("Adding row to table...")
cursor.execute("insert into TestGeometry values (1, :objbv)", objbv=obj)
connection.commit()

View File

@ -1,71 +0,0 @@
# -----------------------------------------------------------------------------
# Copyright (c) 2023, Oracle and/or its affiliates.
#
# This software is dual-licensed to you under the Universal Permissive License
# (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl and Apache License
# 2.0 as shown at http://www.apache.org/licenses/LICENSE-2.0. You may choose
# either license.
#
# If you elect to accept the software under the Apache License, Version 2.0,
# the following applies:
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# -----------------------------------------------------------------------------
# -----------------------------------------------------------------------------
# insert_geometry_async.py
#
# An asynchronous version of insert_geometry.py
#
# Demonstrates the ability to create Oracle objects (this example uses
# SDO_GEOMETRY) and insert them into a table.
# -----------------------------------------------------------------------------
import asyncio
import oracledb
import sample_env
async def main():
connection = await oracledb.connect_async(
user=sample_env.get_main_user(),
password=sample_env.get_main_password(),
dsn=sample_env.get_connect_string(),
)
# create and populate Oracle objects
type_obj = await connection.gettype("MDSYS.SDO_GEOMETRY")
element_info_type_obj = await connection.gettype(
"MDSYS.SDO_ELEM_INFO_ARRAY"
)
ordinate_type_obj = await connection.gettype("MDSYS.SDO_ORDINATE_ARRAY")
obj = type_obj.newobject()
obj.SDO_GTYPE = 2003
obj.SDO_ELEM_INFO = element_info_type_obj.newobject()
obj.SDO_ELEM_INFO.extend([1, 1003, 3])
obj.SDO_ORDINATES = ordinate_type_obj.newobject()
obj.SDO_ORDINATES.extend([1, 1, 5, 7])
print("Created object", obj)
with connection.cursor() as cursor:
await cursor.execute("truncate table TestGeometry")
print("Adding row to table...")
await cursor.execute(
"insert into TestGeometry values (1, :objbv)", objbv=obj
)
await connection.commit()
print("Success!")
asyncio.run(main())

View File

@ -1,5 +1,5 @@
# -----------------------------------------------------------------------------
# Copyright (c) 2020, 2023, Oracle and/or its affiliates.
#------------------------------------------------------------------------------
# Copyright (c) 2020, 2022, Oracle and/or its affiliates.
#
# This software is dual-licensed to you under the Universal Permissive License
# (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl and Apache License
@ -20,9 +20,9 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# -----------------------------------------------------------------------------
#------------------------------------------------------------------------------
# -----------------------------------------------------------------------------
#------------------------------------------------------------------------------
# json_blob.py
#
# Demonstrates how to use a BLOB as a JSON column store.
@ -34,7 +34,7 @@
# Documentation:
# python-oracledb: https://oracledb.readthedocs.io/en/latest/user_guide/json_data_type.html
# Oracle Database: https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=ADJSN
# -----------------------------------------------------------------------------
#------------------------------------------------------------------------------
import json
import sys
@ -46,11 +46,9 @@ import sample_env
if not sample_env.get_is_thin():
oracledb.init_oracle_client(lib_dir=sample_env.get_oracle_client())
connection = oracledb.connect(
user=sample_env.get_main_user(),
password=sample_env.get_main_password(),
dsn=sample_env.get_connect_string(),
)
connection = oracledb.connect(user=sample_env.get_main_user(),
password=sample_env.get_main_password(),
dsn=sample_env.get_connect_string())
if not connection.thin:
client_version = oracledb.clientversion()[0]
@ -62,10 +60,11 @@ if db_version < 12:
# Insert JSON data
with connection.cursor() as cursor:
data = dict(name="Rod", dept="Sales", location="Germany")
inssql = "insert into CustomersAsBlob values (:1, :2)"
if (connection.thin or client_version >= 21) and db_version >= 21:
if not connection.thin and client_version >= 21 and db_version >= 21:
# Take advantage of direct binding
cursor.setinputsizes(None, oracledb.DB_TYPE_JSON)
cursor.execute(inssql, [1, data])
@ -75,40 +74,30 @@ with connection.cursor() as cursor:
# Select JSON data
with connection.cursor() as cursor:
sql = "select c.json_data from CustomersAsBlob c"
for (j,) in cursor.execute(sql):
print(j)
sql = "SELECT c.json_data FROM CustomersAsBlob c"
for j, in cursor.execute(sql):
print(json.loads(j.read()))
# Using JSON_VALUE to extract a value from a JSON column
sql = """select json_value(json_data, '$.location')
from CustomersAsBlob
offset 0 rows fetch next 1 rows only"""
sql = """SELECT JSON_VALUE(json_data, '$.location')
FROM CustomersAsBlob
OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY"""
for r in cursor.execute(sql):
print(r)
# Using dot-notation to extract a value from a JSON (BLOB storage) column
sql = """select c.json_data.location
from CustomersAsBlob c
offset 0 rows fetch next 1 rows only"""
for (j,) in cursor.execute(sql):
sql = """SELECT c.json_data.location
FROM CustomersAsBlob c
OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY"""
for j, in cursor.execute(sql):
print(j)
# Using JSON_OBJECT to extract relational data as JSON
sql = """select json_object('key' is d.dummy) dummy
from dual d"""
for r in cursor.execute(sql):
print(r)
# Using JSON_ARRAYAGG to extract a whole relational table as JSON
oracledb.defaults.fetch_lobs = False
sql = """select json_arrayagg(
json_object('key' is c.id,
'name' is c.json_data)
returning clob)
from CustomersAsBlob c"""
sql = """SELECT JSON_OBJECT('key' IS d.dummy) dummy
FROM dual d"""
for r in cursor.execute(sql):
print(r)

View File

@ -1,121 +0,0 @@
# -----------------------------------------------------------------------------
# Copyright (c) 2023, Oracle and/or its affiliates.
#
# This software is dual-licensed to you under the Universal Permissive License
# (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl and Apache License
# 2.0 as shown at http://www.apache.org/licenses/LICENSE-2.0. You may choose
# either license.
#
# If you elect to accept the software under the Apache License, Version 2.0,
# the following applies:
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# -----------------------------------------------------------------------------
# -----------------------------------------------------------------------------
# json_blob_async.py
#
# An asynchronous version of json_blob.py
#
# Demonstrates how to use a BLOB as a JSON column store.
#
# Note: Oracle Database 12c lets JSON be stored in VARCHAR2 or LOB columns.
# With Oracle Database 21c using the new JSON type is recommended
# instead, see json_direct_async.py
#
# Documentation:
# python-oracledb: https://oracledb.readthedocs.io/en/latest/user_guide/json_data_type.html
# Oracle Database: https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=ADJSN
# -----------------------------------------------------------------------------
import asyncio
import json
import sys
import oracledb
import sample_env
async def main():
connection = await oracledb.connect_async(
user=sample_env.get_main_user(),
password=sample_env.get_main_password(),
dsn=sample_env.get_connect_string(),
)
# Minimum database vesion is 12
db_version = int(connection.version.split(".")[0])
if db_version < 12:
sys.exit("This example requires Oracle Database 12.1.0.2 or later")
# Insert JSON data
with connection.cursor() as cursor:
data = dict(name="Rod", dept="Sales", location="Germany")
inssql = "insert into CustomersAsBlob values (:1, :2)"
if db_version >= 21:
# Take advantage of direct binding
cursor.setinputsizes(None, oracledb.DB_TYPE_JSON)
await cursor.execute(inssql, [1, data])
else:
# Insert the data as a JSON string
await cursor.execute(inssql, [1, json.dumps(data)])
# Select JSON data
with connection.cursor() as cursor:
sql = "select c.json_data from CustomersAsBlob c"
await cursor.execute(sql)
async for (j,) in cursor:
print(j)
# Using JSON_VALUE to extract a value from a JSON column
sql = """select json_value(json_data, '$.location')
from CustomersAsBlob
offset 0 rows fetch next 1 rows only"""
await cursor.execute(sql)
async for (r,) in cursor:
print(r)
# Using dot-notation to extract a value from a JSON (BLOB storage)
# column
sql = """select c.json_data.location
from CustomersAsBlob c
offset 0 rows fetch next 1 rows only"""
await cursor.execute(sql)
async for (j,) in cursor:
print(j)
# Using JSON_OBJECT to extract relational data as JSON
sql = """select json_object('key' is d.dummy) dummy
from dual d"""
await cursor.execute(sql)
async for r in cursor:
print(r)
# Using JSON_ARRAYAGG to extract a whole relational table as JSON
oracledb.defaults.fetch_lobs = False
sql = """select json_arrayagg(
json_object('key' is c.id,
'name' is c.json_data)
returning clob)
from CustomersAsBlob c"""
await cursor.execute(sql)
async for r in cursor:
print(r)
asyncio.run(main())

View File

@ -1,5 +1,5 @@
# -----------------------------------------------------------------------------
# Copyright (c) 2020, 2023, Oracle and/or its affiliates.
#------------------------------------------------------------------------------
# Copyright (c) 2020, 2022, Oracle and/or its affiliates.
#
# This software is dual-licensed to you under the Universal Permissive License
# (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl and Apache License
@ -20,9 +20,9 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# -----------------------------------------------------------------------------
#------------------------------------------------------------------------------
# -----------------------------------------------------------------------------
#------------------------------------------------------------------------------
# json_direct.py
#
# Demonstrates the use of some JSON features with the JSON type that is
@ -31,7 +31,9 @@
# See https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=ADJSN
#
# For JSON with older databases see json_blob.py
# -----------------------------------------------------------------------------
#
# Note: To use the JSON type in python-oracledb thin mode see json_type.py
#------------------------------------------------------------------------------
import json
import sys
@ -39,32 +41,27 @@ import sys
import oracledb
import sample_env
# determine whether to use python-oracledb thin mode or thick mode
if not sample_env.get_is_thin():
oracledb.init_oracle_client(lib_dir=sample_env.get_oracle_client())
# this script is currently only supported in python-oracledb thick mode
oracledb.init_oracle_client(lib_dir=sample_env.get_oracle_client())
connection = oracledb.connect(
user=sample_env.get_main_user(),
password=sample_env.get_main_password(),
dsn=sample_env.get_connect_string(),
)
connection = oracledb.connect(user=sample_env.get_main_user(),
password=sample_env.get_main_password(),
dsn=sample_env.get_connect_string())
if not connection.thin:
client_version = oracledb.clientversion()[0]
client_version = oracledb.clientversion()[0]
db_version = int(connection.version.split(".")[0])
# this script only works with Oracle Database 21
if db_version < 21:
sys.exit(
"This example requires Oracle Database 21.1 or later. "
"Try json_blob.py instead"
)
sys.exit("This example requires Oracle Database 21.1 or later. "
"Try json_blob.py")
# Insert JSON data
with connection.cursor() as cursor:
data = dict(name="Rod", dept="Sales", location="Germany")
inssql = "insert into CustomersAsJson values (:1, :2)"
if connection.thin or client_version >= 21:
if client_version >= 21:
# Take advantage of direct binding
cursor.setinputsizes(None, oracledb.DB_TYPE_JSON)
cursor.execute(inssql, [1, data])
@ -74,12 +71,13 @@ with connection.cursor() as cursor:
# Select JSON data
with connection.cursor() as cursor:
sql = "select c.json_data from CustomersAsJson c"
if connection.thin or client_version >= 21:
for (j,) in cursor.execute(sql):
if client_version >= 21:
for j, in cursor.execute(sql):
print(j)
else:
for (j,) in cursor.execute(sql):
for j, in cursor.execute(sql):
print(json.loads(j.read()))
# Using JSON_VALUE to extract a value from a JSON column
@ -95,11 +93,11 @@ with connection.cursor() as cursor:
sql = """select c.json_data.location
from CustomersAsJson c
offset 0 rows fetch next 1 rows only"""
if connection.thin or client_version >= 21:
for (j,) in cursor.execute(sql):
if client_version >= 21:
for j, in cursor.execute(sql):
print(j)
else:
for (j,) in cursor.execute(sql):
for j, in cursor.execute(sql):
print(json.loads(j.read()))
# Using JSON_OBJECT to extract relational data as JSON
@ -108,14 +106,3 @@ with connection.cursor() as cursor:
from dual d"""
for r in cursor.execute(sql):
print(r)
# Using JSON_ARRAYAGG to extract a whole relational table as JSON
oracledb.defaults.fetch_lobs = False
sql = """select json_arrayagg(
json_object('key' is c.id,
'name' is c.json_data)
returning clob)
from CustomersAsJson c"""
for r in cursor.execute(sql):
print(r)

View File

@ -1,113 +0,0 @@
# -----------------------------------------------------------------------------
# Copyright (c) 2023, Oracle and/or its affiliates.
#
# This software is dual-licensed to you under the Universal Permissive License
# (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl and Apache License
# 2.0 as shown at http://www.apache.org/licenses/LICENSE-2.0. You may choose
# either license.
#
# If you elect to accept the software under the Apache License, Version 2.0,
# the following applies:
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# -----------------------------------------------------------------------------
# -----------------------------------------------------------------------------
# json_direct_async.py
#
# An asynchronous version of json_direct.py
#
# Demonstrates the use of some JSON features with the JSON type that is
# available in Oracle Database 21c and higher.
#
# See https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=ADJSN
#
# For JSON with older databases see json_blob_async.py
# -----------------------------------------------------------------------------
import asyncio
import sys
import oracledb
import sample_env
async def main():
connection = await oracledb.connect_async(
user=sample_env.get_main_user(),
password=sample_env.get_main_password(),
dsn=sample_env.get_connect_string(),
)
# this script only works with Oracle Database 21
db_version = int(connection.version.split(".")[0])
if db_version < 21:
sys.exit(
"This example requires Oracle Database 21.1 or later. "
"Try json_blob.py instead"
)
# Insert JSON data
with connection.cursor() as cursor:
data = dict(name="Rod", dept="Sales", location="Germany")
inssql = "insert into CustomersAsJson values (:1, :2)"
cursor.setinputsizes(None, oracledb.DB_TYPE_JSON)
await cursor.execute(inssql, [1, data])
# Select JSON data
with connection.cursor() as cursor:
sql = "select c.json_data from CustomersAsJson c"
await cursor.execute(sql)
async for (j,) in cursor:
print(j)
# Using JSON_VALUE to extract a value from a JSON column
sql = """select json_value(json_data, '$.location')
from CustomersAsJson
offset 0 rows fetch next 1 rows only"""
await cursor.execute(sql)
async for r in cursor:
print(r)
# Using dot-notation to extract a value from a JSON column
sql = """select c.json_data.location
from CustomersAsJson c
offset 0 rows fetch next 1 rows only"""
await cursor.execute(sql)
async for (j,) in cursor:
print(j)
# Using JSON_OBJECT to extract relational data as JSON
sql = """select json_object('key' is d.dummy) dummy
from dual d"""
await cursor.execute(sql)
async for r in cursor:
print(r)
# Using JSON_ARRAYAGG to extract a whole relational table as JSON
oracledb.defaults.fetch_lobs = False
sql = """select json_arrayagg(
json_object('key' is c.id,
'name' is c.json_data)
returning clob)
from CustomersAsJson c"""
await cursor.execute(sql)
async for r in cursor:
print(r)
asyncio.run(main())

97
samples/json_type.py Normal file
View File

@ -0,0 +1,97 @@
#------------------------------------------------------------------------------
# Copyright (c) 2022, Oracle and/or its affiliates.
#
# This software is dual-licensed to you under the Universal Permissive License
# (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl and Apache License
# 2.0 as shown at http://www.apache.org/licenses/LICENSE-2.0. You may choose
# either license.
#
# If you elect to accept the software under the Apache License, Version 2.0,
# the following applies:
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#------------------------------------------------------------------------------
#------------------------------------------------------------------------------
# json_type.py
#
# Demonstrates storing and fetching the JSON data into/from a Oracle Database
# 21c JSON type column.
#
# In order to use the JSON type in python-oracledb thin mode a type handler is
# needed to fetch the 21c JSON datatype.
#
# Note: The type handler is not needed when using python-oracledb thick mode
# and Oracle Client 21.1 or higher. However, if a type handler is used
# the behavior is the same in python-oracledb thin and thick modes.
#
# This script requires Oracle Database 21.1 or higher.
#------------------------------------------------------------------------------
import json
import sys
import oracledb
import sample_env
# determine whether to use python-oracledb thin mode or thick mode
if not sample_env.get_is_thin():
oracledb.init_oracle_client(lib_dir=sample_env.get_oracle_client())
connection = oracledb.connect(user=sample_env.get_main_user(),
password=sample_env.get_main_password(),
dsn=sample_env.get_connect_string())
if not connection.thin:
client_version = oracledb.clientversion()[0]
db_version = int(connection.version.split(".")[0])
# Minimum database vesion is 21
if db_version < 21:
sys.exit("This example requires Oracle Database 21.1 or later.")
def type_handler(cursor, name, default_type, size, precision, scale):
# to fetch the 21c JSON datatype when using python-oracledb thin mode
if default_type == oracledb.DB_TYPE_JSON:
return cursor.var(str, arraysize=cursor.arraysize,
outconverter=json.loads)
# if using Oracle Client version < 21, then the database returns the
# BLOB data type instead of the JSON data type
elif default_type == oracledb.DB_TYPE_BLOB:
return cursor.var(default_type, arraysize=cursor.arraysize,
outconverter=lambda v: json.loads(v.read()))
# Insert JSON data into a JSON column
with connection.cursor() as cursor:
data = [
(1, dict(name="Rod", dept="Sales", location="Germany")),
(2, dict(name="George", dept="Marketing", location="Bangalore")),
(3, dict(name="Sam", dept="Sales", location="Mumbai")),
(4, dict(name="Jill", dept="Marketing", location="Germany"))
]
insert_sql = "insert into CustomersAsJson values (:1, :2)"
if not connection.thin and client_version >= 21:
# Take advantage of direct binding
cursor.setinputsizes(None, oracledb.DB_TYPE_JSON)
cursor.executemany(insert_sql, data)
else:
# Insert the data as a JSON string
cursor.executemany(insert_sql, [(i, json.dumps(j)) for i, j in data])
# Select JSON data from a JSON column
with connection.cursor() as cursor:
if connection.thin or client_version < 21:
cursor.outputtypehandler = type_handler
for row in cursor.execute("select * from CustomersAsJson"):
print(row)

View File

@ -1,5 +1,5 @@
# -----------------------------------------------------------------------------
# Copyright (c) 2019, 2023, Oracle and/or its affiliates.
#------------------------------------------------------------------------------
# Copyright (c) 2019, 2022, Oracle and/or its affiliates.
#
# This software is dual-licensed to you under the Universal Permissive License
# (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl and Apache License
@ -20,13 +20,13 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# -----------------------------------------------------------------------------
#------------------------------------------------------------------------------
# -----------------------------------------------------------------------------
#------------------------------------------------------------------------------
# last_rowid.py
#
# Demonstrates the use of the cursor.lastrowid attribute.
# -----------------------------------------------------------------------------
#------------------------------------------------------------------------------
import oracledb
import sample_env
@ -35,13 +35,12 @@ import sample_env
if not sample_env.get_is_thin():
oracledb.init_oracle_client(lib_dir=sample_env.get_oracle_client())
connection = oracledb.connect(
user=sample_env.get_main_user(),
password=sample_env.get_main_password(),
dsn=sample_env.get_connect_string(),
)
connection = oracledb.connect(user=sample_env.get_main_user(),
password=sample_env.get_main_password(),
dsn=sample_env.get_connect_string())
with connection.cursor() as cursor:
# insert a couple of rows and retain the rowid of each
row1 = [1, "First"]
row2 = [2, "Second"]
@ -67,9 +66,8 @@ with connection.cursor() as cursor:
# updating multiple rows only returns the rowid of the last updated row
cursor.execute("update mytab set data = data || ' (Modified)'")
cursor.execute(
"select id, data from mytab where rowid = :1", [cursor.lastrowid]
)
cursor.execute("select id, data from mytab where rowid = :1",
[cursor.lastrowid])
print("Last updated row:", cursor.fetchone())
# deleting multiple rows only returns the rowid of the last deleted row
@ -81,4 +79,4 @@ with connection.cursor() as cursor:
print("Rowid when no rows are deleted:", cursor.lastrowid)
# Don't commit - this lets us run the demo multiple times
# connection.commit()
#connection.commit()

View File

@ -1,97 +0,0 @@
# -----------------------------------------------------------------------------
# Copyright (c) 2023, Oracle and/or its affiliates.
#
# This software is dual-licensed to you under the Universal Permissive License
# (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl and Apache License
# 2.0 as shown at http://www.apache.org/licenses/LICENSE-2.0. You may choose
# either license.
#
# If you elect to accept the software under the Apache License, Version 2.0,
# the following applies:
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# -----------------------------------------------------------------------------
# -----------------------------------------------------------------------------
# last_rowid_async.py
#
# An asynchronous version of last_rowid.py
#
# Demonstrates the use of the cursor.lastrowid attribute.
# -----------------------------------------------------------------------------
import asyncio
import oracledb
import sample_env
async def main():
connection = await oracledb.connect_async(
user=sample_env.get_main_user(),
password=sample_env.get_main_password(),
dsn=sample_env.get_connect_string(),
)
with connection.cursor() as cursor:
# insert a couple of rows and retain the rowid of each
row1 = [1, "First"]
row2 = [2, "Second"]
await cursor.execute(
"insert into mytab (id, data) values (:1, :2)", row1
)
rowid1 = cursor.lastrowid
print("Row 1:", row1)
print("Rowid 1:", rowid1)
print()
await cursor.execute(
"insert into mytab (id, data) values (:1, :2)", row2
)
rowid2 = cursor.lastrowid
print("Row 2:", row2)
print("Rowid 2:", rowid2)
print()
# the row can be fetched with the rowid that was returned
await cursor.execute(
"select id, data from mytab where rowid = :1", [rowid1]
)
print("Row 1:", await cursor.fetchone())
await cursor.execute(
"select id, data from mytab where rowid = :1", [rowid2]
)
print("Row 2:", await cursor.fetchone())
print()
# updating multiple rows only returns the rowid of the last updated row
await cursor.execute("update mytab set data = data || ' (Modified)'")
await cursor.execute(
"select id, data from mytab where rowid = :1", [cursor.lastrowid]
)
print("Last updated row:", await cursor.fetchone())
# deleting multiple rows only returns the rowid of the last deleted row
await cursor.execute("delete from mytab")
print("Rowid of last deleted row:", cursor.lastrowid)
# deleting no rows results in a value of None
await cursor.execute("delete from mytab")
print("Rowid when no rows are deleted:", cursor.lastrowid)
# Don't commit - this lets us run the demo multiple times
# await connection.commit()
asyncio.run(main())

View File

@ -1,5 +1,5 @@
# -----------------------------------------------------------------------------
# Copyright (c) 2022, 2023, Oracle and/or its affiliates.
#------------------------------------------------------------------------------
# Copyright (c) 2022, Oracle and/or its affiliates.
#
# This software is dual-licensed to you under the Universal Permissive License
# (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl and Apache License
@ -20,13 +20,13 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# -----------------------------------------------------------------------------
#------------------------------------------------------------------------------
# -----------------------------------------------------------------------------
#------------------------------------------------------------------------------
# load_csv.py
#
# A sample showing how to load CSV data.
# -----------------------------------------------------------------------------
# ------------------------------------------------------------------------------
import csv
import os
@ -40,7 +40,7 @@ if not sample_env.get_is_thin():
# CSV file. This sample file has both valid rows and some rows with data too
# large to insert.
FILE_NAME = os.path.join("data", "load_csv.csv")
FILE_NAME = os.path.join('data', 'load_csv.csv')
# Adjust the number of rows to be inserted in each iteration to meet your
# memory and performance requirements. Typically this is a large-ish value to
@ -49,12 +49,9 @@ FILE_NAME = os.path.join("data", "load_csv.csv")
# behavior of the code.
BATCH_SIZE = 19
connection = oracledb.connect(
user=sample_env.get_main_user(),
password=sample_env.get_main_password(),
dsn=sample_env.get_connect_string(),
)
connection = oracledb.connect(user=sample_env.get_main_user(),
password=sample_env.get_main_password(),
dsn=sample_env.get_connect_string())
def process_batch(batch_number, cursor, data):
print("processing batch", batch_number + 1)
@ -63,10 +60,10 @@ def process_batch(batch_number, cursor, data):
line_num = (batch_number * BATCH_SIZE) + error.offset + 1
print("Error", error.message, "at line", line_num)
with connection.cursor() as cursor:
# Clean up the table for demonstration purposes
cursor.execute("truncate table LoadCsvTab")
cursor.execute('truncate table LoadCsvTab');
# Predefine the memory areas to match the table definition.
# This can improve performance by avoiding memory reallocations.
@ -77,8 +74,8 @@ with connection.cursor() as cursor:
cursor.setinputsizes(None, 25)
# Loop over the data and insert it in batches
with open(FILE_NAME, "r") as csv_file:
csv_reader = csv.reader(csv_file, delimiter=",")
with open(FILE_NAME, 'r') as csv_file:
csv_reader = csv.reader(csv_file, delimiter=',')
sql = "insert into LoadCsvTab (id, name) values (:1, :2)"
data = []
batch_number = 0

View File

@ -1,100 +0,0 @@
# -----------------------------------------------------------------------------
# Copyright (c) 2023, Oracle and/or its affiliates.
#
# This software is dual-licensed to you under the Universal Permissive License
# (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl and Apache License
# 2.0 as shown at http://www.apache.org/licenses/LICENSE-2.0. You may choose
# either license.
#
# If you elect to accept the software under the Apache License, Version 2.0,
# the following applies:
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# -----------------------------------------------------------------------------
# -----------------------------------------------------------------------------
# load_csv_async.py
#
# An asynchronous version of load_csv.py
#
# A sample showing how to load CSV data.
# -----------------------------------------------------------------------------
import asyncio
import csv
import os
import oracledb
import sample_env
# CSV file. This sample file has both valid rows and some rows with data too
# large to insert.
FILE_NAME = os.path.join("data", "load_csv.csv")
# Adjust the number of rows to be inserted in each iteration to meet your
# memory and performance requirements. Typically this is a large-ish value to
# reduce the number of calls to executemany() to a reasonable size. For this
# demo with a small CSV file a smaller number is used to show the looping
# behavior of the code.
BATCH_SIZE = 19
async def main():
connection = await oracledb.connect_async(
user=sample_env.get_main_user(),
password=sample_env.get_main_password(),
dsn=sample_env.get_connect_string(),
)
async def process_batch(batch_number, cursor, data):
print("processing batch", batch_number + 1)
await cursor.executemany(sql, data, batcherrors=True)
for error in cursor.getbatcherrors():
line_num = (batch_number * BATCH_SIZE) + error.offset + 1
print("Error", error.message, "at line", line_num)
with connection.cursor() as cursor:
# Clean up the table for demonstration purposes
await cursor.execute("truncate table LoadCsvTab")
# Predefine the memory areas to match the table definition.
# This can improve performance by avoiding memory reallocations.
# Here, one parameter is passed for each of the columns.
# "None" is used for the ID column, since the size of NUMBER isn't
# variable. The "25" matches the maximum expected data size for the
# NAME column
cursor.setinputsizes(None, 25)
# Loop over the data and insert it in batches
with open(FILE_NAME, "r") as csv_file:
csv_reader = csv.reader(csv_file, delimiter=",")
sql = "insert into LoadCsvTab (id, name) values (:1, :2)"
data = []
batch_number = 0
for line in csv_reader:
data.append((line[0], line[1]))
if len(data) % BATCH_SIZE == 0:
await process_batch(batch_number, cursor, data)
data = []
batch_number += 1
if data:
await process_batch(batch_number, cursor, data)
# In a production system you might choose to fix any invalid rows,
# re-insert them, and then commit. Or you could rollback
# everything. In this sample we simply commit and ignore the
# invalid rows that couldn't be inserted.
await connection.commit()
asyncio.run(main())

View File

@ -1,5 +1,5 @@
# -----------------------------------------------------------------------------
# Copyright (c) 2020, 2023, Oracle and/or its affiliates.
#------------------------------------------------------------------------------
# Copyright (c) 2020, 2022, Oracle and/or its affiliates.
#
# Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved.
#
@ -25,14 +25,14 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# -----------------------------------------------------------------------------
#------------------------------------------------------------------------------
# -----------------------------------------------------------------------------
#------------------------------------------------------------------------------
# multi_consumer_aq.py
#
# Demonstrates how to use multi-consumer advanced queuing. It makes use of a
# RAW queue created in the sample setup.
# -----------------------------------------------------------------------------
#------------------------------------------------------------------------------
import oracledb
import sample_env
@ -45,15 +45,13 @@ PAYLOAD_DATA = [
"The first message",
"The second message",
"The third message",
"The fourth and final message",
"The fourth and final message"
]
# connect to database
connection = oracledb.connect(
user=sample_env.get_main_user(),
password=sample_env.get_main_password(),
dsn=sample_env.get_connect_string(),
)
connection = oracledb.connect(user=sample_env.get_main_user(),
password=sample_env.get_main_password(),
dsn=sample_env.get_connect_string())
# create a queue
queue = connection.queue(QUEUE_NAME)

View File

@ -1,5 +1,5 @@
# -----------------------------------------------------------------------------
# Copyright (c) 2016, 2023, Oracle and/or its affiliates.
#------------------------------------------------------------------------------
# Copyright (c) 2016, 2022, Oracle and/or its affiliates.
#
# Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved.
#
@ -25,14 +25,14 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# -----------------------------------------------------------------------------
#------------------------------------------------------------------------------
# -----------------------------------------------------------------------------
#------------------------------------------------------------------------------
# object_aq.py
#
# Demonstrates how to use advanced queuing with objects. It makes use of a
# simple type and queue created in the sample setup.
# -----------------------------------------------------------------------------
#------------------------------------------------------------------------------
import decimal
@ -45,24 +45,16 @@ oracledb.init_oracle_client(lib_dir=sample_env.get_oracle_client())
BOOK_TYPE_NAME = "UDT_BOOK"
QUEUE_NAME = "DEMO_BOOK_QUEUE"
BOOK_DATA = [
(
"The Fellowship of the Ring",
"Tolkien, J.R.R.",
decimal.Decimal("10.99"),
),
(
"Harry Potter and the Philosopher's Stone",
"Rowling, J.K.",
decimal.Decimal("7.99"),
),
("The Fellowship of the Ring", "Tolkien, J.R.R.",
decimal.Decimal("10.99")),
("Harry Potter and the Philosopher's Stone", "Rowling, J.K.",
decimal.Decimal("7.99"))
]
# connect to database
connection = oracledb.connect(
user=sample_env.get_main_user(),
password=sample_env.get_main_password(),
dsn=sample_env.get_connect_string(),
)
connection = oracledb.connect(user=sample_env.get_main_user(),
password=sample_env.get_main_password(),
dsn=sample_env.get_connect_string())
# create a queue
books_type = connection.gettype(BOOK_TYPE_NAME)

View File

@ -1,111 +0,0 @@
# -----------------------------------------------------------------------------
# Copyright (c) 2023, Oracle and/or its affiliates.
#
# This software is dual-licensed to you under the Universal Permissive License
# (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl and Apache License
# 2.0 as shown at http://www.apache.org/licenses/LICENSE-2.0. You may choose
# either license.
#
# If you elect to accept the software under the Apache License, Version 2.0,
# the following applies:
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# -----------------------------------------------------------------------------
# -----------------------------------------------------------------------------
# object_dump.py
#
# Shows how to pretty-print an Oracle object or collection.
# Also shows how to insert a Python object to an Oracle object column.
# -----------------------------------------------------------------------------
import oracledb
import sample_env
# determine whether to use python-oracledb thin mode or thick mode
if not sample_env.get_is_thin():
oracledb.init_oracle_client(lib_dir=sample_env.get_oracle_client())
# Create Oracle connection and cursor objects
connection = oracledb.connect(
user=sample_env.get_main_user(),
password=sample_env.get_main_password(),
dsn=sample_env.get_connect_string(),
)
cursor = connection.cursor()
# Create a Python class equivalent to an Oracle SDO object
class MySDO(object):
def __init__(self, gtype, elem_info, ordinates):
self.gtype = gtype
self.elem_info = elem_info
self.ordinates = ordinates
# Get Oracle type information
obj_type = connection.gettype("MDSYS.SDO_GEOMETRY")
element_info_type_obj = connection.gettype("MDSYS.SDO_ELEM_INFO_ARRAY")
ordinate_type_obj = connection.gettype("MDSYS.SDO_ORDINATE_ARRAY")
# Convert a Python object to MDSYS.SDO_GEOMETRY
def sdo_input_type_handler(cursor, value, num_elements):
def sdo_in_converter(value):
obj = obj_type.newobject()
obj.SDO_GTYPE = value.gtype
obj.SDO_ELEM_INFO = element_info_type_obj.newobject()
obj.SDO_ELEM_INFO.extend(value.elem_info)
obj.SDO_ORDINATES = ordinate_type_obj.newobject()
obj.SDO_ORDINATES.extend(value.ordinates)
return obj
if isinstance(value, MySDO):
return cursor.var(
obj_type, arraysize=num_elements, inconverter=sdo_in_converter
)
# Create and insert a Python object
sdo = MySDO(2003, [1, 1003, 3], [1, 1, 5, 7])
cursor.inputtypehandler = sdo_input_type_handler
cursor.execute("truncate table TestGeometry")
cursor.execute("insert into TestGeometry values (1, :1)", [sdo])
# Define a function to pretty-print the contents of an Oracle object
def dump_object(obj, prefix=""):
if obj.type.iscollection:
print(f"{prefix}[")
for value in obj.aslist():
if isinstance(value, oracledb.DbObject):
dump_object(value, prefix + " ")
else:
print(f"{prefix} {repr(value)}")
print(f"{prefix}]")
else:
print(f"{prefix}{{")
for attr in obj.type.attributes:
value = getattr(obj, attr.name)
if isinstance(value, oracledb.DbObject):
print(f"{prefix} {attr.name}:")
dump_object(value, prefix + " ")
else:
print(f"{prefix} {attr.name}: {repr(value)}")
print(f"{prefix}}}")
# Query the row back
cursor.execute("select geometry from TestGeometry")
for (obj,) in cursor:
dump_object(obj)

Some files were not shown because too many files have changed in this diff Show More