Compare commits
No commits in common. "main" and "gh-pages" have entirely different histories.
|
@ -1,8 +0,0 @@
|
|||
* text=auto
|
||||
|
||||
*.c text
|
||||
*.h text
|
||||
*.in text
|
||||
*.py text
|
||||
*.rst text
|
||||
*.txt text
|
|
@ -1,10 +0,0 @@
|
|||
---
|
||||
name: Announcements
|
||||
about: Use this if you are sharing something interesting
|
||||
title: ''
|
||||
labels: announcement
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
<!-- Let us know if you are speaking at a conference on cx_Oracle, or have a new package or app that uses cx_Oracle, or something similarly exciting. -->
|
|
@ -1,70 +0,0 @@
|
|||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: bug
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
<!--
|
||||
|
||||
Thank you for using cx_Oracle.
|
||||
|
||||
See https://www.oracle.com/corporate/security-practices/assurance/vulnerability/reporting.html for how to report security issues
|
||||
|
||||
Please answer these questions so we can help you.
|
||||
|
||||
Use Markdown syntax, see https://docs.github.com/github/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax
|
||||
|
||||
-->
|
||||
|
||||
1. What versions are you using?
|
||||
|
||||
<!--
|
||||
|
||||
Give your database version.
|
||||
|
||||
|
||||
Also run Python and show the output of:
|
||||
|
||||
import sys
|
||||
import platform
|
||||
|
||||
print("platform.platform:", platform.platform())
|
||||
print("sys.maxsize > 2**32:", sys.maxsize > 2**32)
|
||||
print("platform.python_version:", platform.python_version())
|
||||
|
||||
And:
|
||||
|
||||
import cx_Oracle
|
||||
print("cx_Oracle.version:", cx_Oracle.version)
|
||||
print("cx_Oracle.clientversion:", cx_Oracle.clientversion())
|
||||
|
||||
-->
|
||||
|
||||
2. Is it an error or a hang or a crash?
|
||||
|
||||
3. What error(s) or behavior you are seeing?
|
||||
|
||||
<!--
|
||||
|
||||
Cut and paste text showing the command you ran. No screenshots.
|
||||
|
||||
Use a gist for long screen output and logs: see https://gist.github.com/
|
||||
|
||||
-->
|
||||
|
||||
4. Include a runnable Python script that shows the problem.
|
||||
|
||||
<!--
|
||||
|
||||
Include all SQL needed to create the database schema.
|
||||
|
||||
Format code by using three backticks on a line before and after code snippets, for example:
|
||||
|
||||
```
|
||||
import cx_Oracle
|
||||
```
|
||||
|
||||
-->
|
|
@ -1 +0,0 @@
|
|||
blank_issues_enabled: false
|
|
@ -1,24 +0,0 @@
|
|||
---
|
||||
name: Documentation and Example Improvements
|
||||
about: Use this to suggest changes to documentation and examples
|
||||
title: ''
|
||||
labels: enhancement
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
<!--
|
||||
|
||||
Thank you for using cx_Oracle.
|
||||
|
||||
Please answer these questions so we can help you.
|
||||
|
||||
Use Markdown syntax, see https://docs.github.com/github/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax
|
||||
|
||||
-->
|
||||
|
||||
1. What is the link to the documentation section that needs improving?
|
||||
|
||||
2. Describe the confusion
|
||||
|
||||
3. Suggest changes that would help
|
|
@ -1,24 +0,0 @@
|
|||
---
|
||||
name: Enhancement Requests
|
||||
about: Use this for enhancement requests
|
||||
title: ''
|
||||
labels: enhancement
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
<!--
|
||||
|
||||
Thank you for using cx_Oracle.
|
||||
|
||||
Review existing enhancement requests: https://github.com/oracle/python-cx_Oracle/labels/enhancement
|
||||
|
||||
Please answer these questions so we can help you.
|
||||
|
||||
Use Markdown syntax, see https://docs.github.com/github/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax
|
||||
|
||||
-->
|
||||
|
||||
1. Describe your new request in detail
|
||||
|
||||
2. Give supporting information about tools and operating systems. Give relevant product version numbers
|
|
@ -1,65 +0,0 @@
|
|||
---
|
||||
name: Questions and Runtime Problems
|
||||
about: For general cx_Oracle questions
|
||||
title: ''
|
||||
labels: question
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
<!--
|
||||
|
||||
Thank you for using cx_Oracle.
|
||||
|
||||
Review the user manual: https://cx-oracle.readthedocs.io/en/latest/index.html
|
||||
|
||||
Please answer these questions so we can help you.
|
||||
|
||||
Use Markdown syntax, see https://docs.github.com/github/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax
|
||||
|
||||
GitHub issues that are not updated for a month may be automatically closed. Feel free to update them at any time.
|
||||
|
||||
-->
|
||||
|
||||
1. What versions are you using?
|
||||
|
||||
<!--
|
||||
|
||||
Give your database version.
|
||||
|
||||
Also run Python and show the output of:
|
||||
|
||||
import sys
|
||||
import platform
|
||||
|
||||
print("platform.platform:", platform.platform())
|
||||
print("sys.maxsize > 2**32:", sys.maxsize > 2**32)
|
||||
print("platform.python_version:", platform.python_version())
|
||||
|
||||
And:
|
||||
|
||||
import cx_Oracle
|
||||
print("cx_Oracle.version:", cx_Oracle.version)
|
||||
print("cx_Oracle.clientversion:", cx_Oracle.clientversion())
|
||||
|
||||
-->
|
||||
|
||||
2. Describe the problem
|
||||
|
||||
<!-- Cut and paste text showing the command you ran. No screenshots. -->
|
||||
|
||||
3. Include a runnable Python script that shows the problem.
|
||||
|
||||
<!--
|
||||
|
||||
Include all SQL needed to create the database schema.
|
||||
|
||||
Use a gist for long code: see https://gist.github.com/
|
||||
|
||||
Format code by using three backticks on a line before and after code snippets, for example:
|
||||
|
||||
```
|
||||
import cx_Oracle
|
||||
```
|
||||
|
||||
-->
|
|
@ -1,57 +0,0 @@
|
|||
---
|
||||
name: Installation Problems
|
||||
about: Use this for cx_Oracle installation questions
|
||||
title: ''
|
||||
labels: install & configuration
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
<!--
|
||||
|
||||
Thank you for using cx_Oracle.
|
||||
|
||||
Do these before creating a new issue:
|
||||
|
||||
Review and follow the Installation Instructions: https://cx-oracle.readthedocs.io/en/latest/user_guide/installation.html
|
||||
|
||||
Review the troubleshooting tips: https://cx-oracle.readthedocs.io/en/latest/user_guide/installation.html#troubleshooting
|
||||
|
||||
Review the user manual: https://cx-oracle.readthedocs.io/en/latest/index.html
|
||||
|
||||
If you have a `DPI-1047`, `DPI-1050` or `DPI-1072` error, re-review the links above.
|
||||
|
||||
Google any errors.
|
||||
|
||||
Then please answer these questions so we can help you.
|
||||
|
||||
GitHub issues that are not updated for a month may be automatically closed. Feel free to update them at any time.
|
||||
|
||||
-->
|
||||
|
||||
1. What versions are you using?
|
||||
|
||||
<!--
|
||||
|
||||
Give your database version.
|
||||
|
||||
Also run Python and show the output of:
|
||||
|
||||
import sys
|
||||
import platform
|
||||
|
||||
print("platform.platform:", platform.platform())
|
||||
print("sys.maxsize > 2**32:", sys.maxsize > 2**32)
|
||||
print("platform.python_version:", platform.python_version())
|
||||
|
||||
-->
|
||||
|
||||
2. Describe the problem
|
||||
|
||||
<!-- Cut and paste text showing the command you ran. No screenshots. -->
|
||||
|
||||
3. Show the directory listing where your Oracle Client libraries are installed (e.g. the Instant Client directory). Is it 64-bit or 32-bit?
|
||||
|
||||
4. Show what the `PATH` environment variable (on Windows) or `LD_LIBRARY_PATH` (on Linux) is set to?
|
||||
|
||||
5. Show any Oracle environment variables set (e.g. ORACLE_HOME, ORACLE_BASE).
|
|
@ -1,13 +0,0 @@
|
|||
# Reporting Security Vulnerabilities
|
||||
|
||||
Oracle values the independent security research community and believes that responsible disclosure of security vulnerabilities helps us ensure the security 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 preferably with a proof of concept. We provide additional information on [how to report security vulnerabilities to Oracle](https://www.oracle.com/corporate/security-practices/assurance/vulnerability/reporting.html) which includes public encryption keys for secure email.
|
||||
|
||||
We ask that you do not use other channels or contact project contributors directly.
|
||||
|
||||
Non-vulnerability related security issues such as great new ideas for security features are welcome on GitHub Issues.
|
||||
|
||||
## 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.
|
|
@ -1,32 +0,0 @@
|
|||
# Python cx_Oracle Support
|
||||
|
||||
cx_Oracle is an Open Source project, so do some searching and reading
|
||||
before asking questions.
|
||||
|
||||
## cx_Oracle Installation issues
|
||||
|
||||
Read the [Installation instructions](http://cx-oracle.readthedocs.io/en/latest/installation.html)
|
||||
|
||||
## SQL and PL/SQL Questions
|
||||
|
||||
Ask SQL and PL/SQL questions at [AskTOM](https://asktom.oracle.com/)
|
||||
|
||||
Try out SQL and find code snippets on our hosted database
|
||||
with [LIVE SQL](https://livesql.oracle.com/)
|
||||
|
||||
## Database and other Oracle Issues
|
||||
|
||||
Ask Database and other Oracle issues on
|
||||
an [OTN Forum](https://community.oracle.com/community/database/)
|
||||
|
||||
## cx_Oracle Documentation
|
||||
|
||||
The cx_Oracle documentation
|
||||
is [here](http://cx-oracle.readthedocs.io/en/latest/)
|
||||
|
||||
## Got a cx_Oracle question?
|
||||
|
||||
Ask at [GitHub](https://github.com/oracle/python-cx_Oracle/issues)
|
||||
|
||||
When opening a new issue, fill in the template that will be shown.
|
||||
Include enough information for people to understand your problem.
|
|
@ -1,20 +0,0 @@
|
|||
Thanks for contributing!
|
||||
|
||||
Before submitting PRs for cx_Oracle you must have your signed *Oracle
|
||||
Contributor Agreement* accepted. See https://oca.opensource.oracle.com
|
||||
|
||||
If the problem solved is small, you may find it easier to open an Issue
|
||||
describing the problem and its cause so we can create the fix.
|
||||
|
||||
The bottom of your commit message must have the following line using your name
|
||||
and e-mail address as it appears in the OCA Signatories list.
|
||||
|
||||
```
|
||||
Signed-off-by: Your Name <you@example.org>
|
||||
```
|
||||
|
||||
This can be automatically added to pull requests by committing with:
|
||||
|
||||
```
|
||||
git commit --signoff
|
||||
````
|
|
@ -1,23 +0,0 @@
|
|||
# https://probot.github.io/apps/stale/
|
||||
|
||||
# Number of days of inactivity before an issue becomes stale
|
||||
daysUntilStale: 30
|
||||
# Number of days of inactivity before a stale issue is closed
|
||||
daysUntilClose: 7
|
||||
# Issues with these labels will never be considered stale
|
||||
exemptLabels:
|
||||
- pinned
|
||||
- enhancement
|
||||
- bug
|
||||
- announcement
|
||||
- OCA accepted
|
||||
# Label to use when marking an issue as stale
|
||||
staleLabel: inactive
|
||||
# Comment to post when marking an issue as stale. Set to `false` to disable
|
||||
markComment: >
|
||||
This issue has been automatically marked as inactive because it has not been
|
||||
updated recently. It will be closed if no further activity occurs. Thank you
|
||||
for your contributions.
|
||||
# Comment to post when closing a stale issue. Set to `false` to disable
|
||||
closeComment: >
|
||||
This issue has been automatically closed because it has not been updated for a month.
|
|
@ -1,7 +0,0 @@
|
|||
*.pyc
|
||||
.tox/
|
||||
build/
|
||||
dist/
|
||||
doc/build
|
||||
cx_Oracle.egg-info/
|
||||
MANIFEST
|
|
@ -1,3 +0,0 @@
|
|||
[submodule "odpi"]
|
||||
path = odpi
|
||||
url = ../odpi.git
|
|
@ -1,16 +0,0 @@
|
|||
# required
|
||||
version: 2
|
||||
|
||||
build:
|
||||
os: ubuntu-20.04
|
||||
tools:
|
||||
python: "3.9"
|
||||
|
||||
# Build documentation in the doc/src directory with Sphinx
|
||||
sphinx:
|
||||
configuration: doc/src/conf.py
|
||||
|
||||
# declare Python requirements required to build docs
|
||||
python:
|
||||
install:
|
||||
- requirements: doc/requirements.txt
|
|
@ -1,44 +0,0 @@
|
|||
# Contributing
|
||||
|
||||
We welcome your contributions! There are multiple ways to contribute.
|
||||
|
||||
## 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 on our [Security Policy](./.github/SECURITY.md).
|
||||
|
||||
## Contributing Code
|
||||
|
||||
We welcome your code contributions. 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:
|
||||
|
||||
```text
|
||||
git commit --signoff
|
||||
```
|
||||
|
||||
Only pull requests from committers that can be verified as having
|
||||
signed the OCA can be accepted.
|
||||
|
||||
### Pull request process
|
||||
|
||||
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`
|
||||
1. Ensure that any documentation is updated with the changes that are required
|
||||
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 review your PR before it is merged.
|
||||
|
||||
## 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](https://www.contributor-covenant.org/version/1/4/code-of-conduct/)
|
36
LICENSE.txt
36
LICENSE.txt
|
@ -1,36 +0,0 @@
|
|||
LICENSE AGREEMENT FOR CX_ORACLE
|
||||
|
||||
Copyright 2016, 2018, 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.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions, and the disclaimer that follows.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions, and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the names of the copyright holders nor the names of any contributors
|
||||
may be used to endorse or promote products derived from this software
|
||||
without specific prior written permission.
|
||||
|
||||
DISCLAIMER: THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
*AS IS* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 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.
|
||||
|
||||
Computronix is a registered trademark of Computronix (Canada) Ltd.
|
||||
|
10
MANIFEST.in
10
MANIFEST.in
|
@ -1,10 +0,0 @@
|
|||
include MANIFEST.in
|
||||
include *.txt
|
||||
recursive-include odpi *.c
|
||||
recursive-include odpi *.h
|
||||
prune odpi/test
|
||||
prune odpi/samples
|
||||
recursive-include src *.c
|
||||
recursive-include src *.h
|
||||
recursive-include samples *.py *.sql
|
||||
recursive-include test *.py *.sql
|
73
README.md
73
README.md
|
@ -1,73 +0,0 @@
|
|||
# Python cx_Oracle
|
||||
|
||||
# News
|
||||
|
||||
**cx_Oracle has a major new release under a new name and homepage
|
||||
[python-oracledb](https://oracle.github.io/python-oracledb/).**
|
||||
|
||||
**The source code has moved to
|
||||
[github.com/oracle/python-oracledb](https://github.com/oracle/python-oracledb).**
|
||||
|
||||
New projects should install python-oracledb instead of cx_Oracle. Critical
|
||||
patches and binary packages for new Python releases may continue to be made in
|
||||
the cx_Oracle namespace for a limited time, subject to demand.
|
||||
|
||||
# About
|
||||
|
||||
cx_Oracle is a Python extension module that enables access to Oracle
|
||||
Database. It conforms to the [Python database API 2.0
|
||||
specification][1] with a considerable number of additions and a couple
|
||||
of exclusions. See the
|
||||
[homepage](https://oracle.github.io/python-cx_Oracle/index.html) for a
|
||||
feature list.
|
||||
|
||||
cx_Oracle 8.3 was tested with Python versions 3.6 through 3.10. You can
|
||||
use cx_Oracle with Oracle 11.2, 12c, 18c, 19c and 21c client libraries.
|
||||
Oracle's standard client-server version interoperability allows connection to
|
||||
both older and newer databases. For example Oracle 19c client libraries can
|
||||
connect to Oracle Database 11.2. Older versions of cx_Oracle may work with
|
||||
older versions of Python.
|
||||
|
||||
## Installation
|
||||
|
||||
See [cx_Oracle Installation][15].
|
||||
|
||||
## Documentation
|
||||
|
||||
See the [cx_Oracle Documentation][2] and [Release Notes][14].
|
||||
|
||||
## Samples
|
||||
|
||||
See the [/samples][12] directory and the [tutorial][6]. You can also
|
||||
look at the scripts in [cx_OracleTools][7] and the modules in
|
||||
[cx_PyOracleLib][8].
|
||||
|
||||
## Help
|
||||
|
||||
Issues and questions can be raised with the cx_Oracle community on
|
||||
[GitHub][9] or on the [mailing list][5].
|
||||
|
||||
## Tests
|
||||
|
||||
See [/test][11].
|
||||
|
||||
## Contributing
|
||||
|
||||
See [CONTRIBUTING](https://github.com/oracle/python-cx_Oracle/blob/main/CONTRIBUTING.md)
|
||||
|
||||
## License
|
||||
|
||||
cx_Oracle is licensed under a BSD license which you can find [here][3].
|
||||
|
||||
[1]: https://peps.python.org/pep-0249/
|
||||
[2]: https://cx-oracle.readthedocs.io
|
||||
[3]: https://github.com/oracle/python-cx_Oracle/blob/main/LICENSE.txt
|
||||
[5]: https://sourceforge.net/projects/cx-oracle/lists/cx-oracle-users
|
||||
[6]: https://github.com/oracle/python-cx_Oracle/tree/main/samples/tutorial
|
||||
[7]: http://cx-oracletools.sourceforge.net
|
||||
[8]: http://cx-pyoraclelib.sourceforge.net
|
||||
[9]: https://github.com/oracle/python-cx_Oracle/issues
|
||||
[11]: https://github.com/oracle/python-cx_Oracle/tree/main/test
|
||||
[12]: https://github.com/oracle/python-cx_Oracle/tree/main/samples
|
||||
[14]: https://cx-oracle.readthedocs.io/en/latest/release_notes.html
|
||||
[15]: https://cx-oracle.readthedocs.io/en/latest/user_guide/installation.html
|
|
@ -1,7 +0,0 @@
|
|||
An enhanced cx_Oracle release is now under the python-oracledb namespace. See
|
||||
https://oracle.github.io/python-oracledb/index.html for how to install and use
|
||||
this updated driver.
|
||||
|
||||
For information about cx_Oracle itself, see
|
||||
https://oracle.github.io/python-cx_Oracle/index.html
|
||||
|
23
doc/Makefile
23
doc/Makefile
|
@ -1,23 +0,0 @@
|
|||
# Makefile to generate cx_Oracle documentation using Sphinx
|
||||
|
||||
SPHINXOPTS =
|
||||
SPHINXBUILD = sphinx-build
|
||||
SOURCEDIR = src
|
||||
BUILDDIR = build
|
||||
|
||||
.PHONY: html
|
||||
html:
|
||||
@$(SPHINXBUILD) -M html $(SOURCEDIR) $(BUILDDIR) $(SPHINXOPTS)
|
||||
|
||||
.PHONY: epub
|
||||
epub:
|
||||
@$(SPHINXBUILD) -M epub $(SOURCEDIR) $(BUILDDIR) $(SPHINXOPTS)
|
||||
|
||||
.PHONY: pdf
|
||||
pdf:
|
||||
@$(SPHINXBUILD) -M latexpdf $(SOURCEDIR) $(BUILDDIR) $(SPHINXOPTS)
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm -rf $(BUILDDIR)/*
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
The generated cx_Oracle documentation is at http://cx-oracle.readthedocs.io/
|
||||
|
||||
This directory contains the documentation source. It is written using reST
|
||||
(re-Structured Text) format source files which are processed using Sphinx and
|
||||
turned into HTML, PDF or ePub documents. If you wish to build these yourself,
|
||||
you need to install Sphinx. Sphinx is available on many Linux distributions as a
|
||||
pre-built package. You can also install Sphinx on all platforms using the Python
|
||||
package manager "pip". For more information on Sphinx, please visit this page:
|
||||
|
||||
http://www.sphinx-doc.org
|
||||
|
||||
Once Sphinx is installed, the supplied Makefile can be used to build the
|
||||
different targets.
|
|
@ -1,2 +0,0 @@
|
|||
sphinx>=4.2.0
|
||||
sphinx-rtd-theme>=0.5.2
|
|
@ -1,358 +0,0 @@
|
|||
.. _aq:
|
||||
|
||||
*********************
|
||||
Advanced Queuing (AQ)
|
||||
*********************
|
||||
|
||||
.. note::
|
||||
|
||||
**cx_Oracle has a major new release under a new name and homepage**
|
||||
`python-oracledb <https://oracle.github.io/python-oracledb/>`__.
|
||||
|
||||
**New projects should install python-oracledb instead of cx_Oracle.**
|
||||
|
||||
See :ref:`aqusermanual` for more information about using AQ in cx_Oracle.
|
||||
|
||||
.. note::
|
||||
|
||||
All of these objects are extensions to the DB API.
|
||||
|
||||
.. _queue:
|
||||
|
||||
------
|
||||
Queues
|
||||
------
|
||||
|
||||
These objects are created using the :meth:`Connection.queue()` method and are
|
||||
used to enqueue and dequeue messages.
|
||||
|
||||
.. attribute:: Queue.connection
|
||||
|
||||
This read-only attribute returns a reference to the connection object on
|
||||
which the queue was created.
|
||||
|
||||
|
||||
.. method:: Queue.deqmany(maxMessages)
|
||||
|
||||
Dequeues up to the specified number of messages from the queue and returns
|
||||
a list of these messages. Each element of the returned list is a
|
||||
:ref:`message property<msgproperties>` object.
|
||||
|
||||
.. versionchanged:: 8.2
|
||||
|
||||
For consistency and compliance with the PEP 8 naming style, the name of
|
||||
the method was changed from `deqMany()`. The old name will continue to
|
||||
work for a period of time.
|
||||
|
||||
.. method:: Queue.deqone()
|
||||
|
||||
Dequeues at most one message from the queue. If a message is dequeued, it
|
||||
will be a :ref:`message property<msgproperties>` object; otherwise, it will
|
||||
be the value None.
|
||||
|
||||
.. versionchanged:: 8.2
|
||||
|
||||
For consistency and compliance with the PEP 8 naming style, the name of
|
||||
the method was changed from `deqOne()`. The old name will continue to
|
||||
work for a period of time.
|
||||
|
||||
.. attribute:: Queue.deqoptions
|
||||
|
||||
This read-only attribute returns a reference to the :ref:`options
|
||||
<deqoptions>` that will be used when dequeuing messages from the queue.
|
||||
|
||||
.. versionchanged:: 8.2
|
||||
|
||||
For consistency and compliance with the PEP 8 naming style, the name of
|
||||
the attribute was changed from `deqOptions`. The old name will continue
|
||||
to work for a period of time.
|
||||
|
||||
.. method:: Queue.enqmany(messages)
|
||||
|
||||
Enqueues multiple messages into the queue. The messages parameter must be a
|
||||
sequence containing :ref:`message property <msgproperties>` objects which
|
||||
have all had their payload attribute set to a value that the queue
|
||||
supports.
|
||||
|
||||
Warning: calling this function in parallel on different connections
|
||||
acquired from the same pool may fail due to Oracle bug 29928074. Ensure
|
||||
that this function is not run in parallel, use standalone connections or
|
||||
connections from different pools, or make multiple calls to
|
||||
:meth:`Queue.enqone()` instead. The function :meth:`Queue.deqmany()`
|
||||
call is not affected.
|
||||
|
||||
.. versionchanged:: 8.2
|
||||
|
||||
For consistency and compliance with the PEP 8 naming style, the name of
|
||||
the method was changed from `enqMany()`. The old name will continue
|
||||
to work for a period of time.
|
||||
|
||||
|
||||
.. method:: Queue.enqone(message)
|
||||
|
||||
Enqueues a single message into the queue. The message must be a
|
||||
:ref:`message property<msgproperties>` object which has had its payload
|
||||
attribute set to a value that the queue supports.
|
||||
|
||||
.. versionchanged:: 8.2
|
||||
|
||||
For consistency and compliance with the PEP 8 naming style, the name of
|
||||
the method was changed from `enqOne()`. The old name will continue
|
||||
to work for a period of time.
|
||||
|
||||
.. attribute:: Queue.enqoptions
|
||||
|
||||
This read-only attribute returns a reference to the :ref:`options
|
||||
<enqoptions>` that will be used when enqueuing messages into the queue.
|
||||
|
||||
.. versionchanged:: 8.2
|
||||
|
||||
For consistency and compliance with the PEP 8 naming style, the name of
|
||||
the attribute was changed from `enqOptions`. The old name will continue
|
||||
to work for a period of time.
|
||||
|
||||
|
||||
.. attribute:: Queue.name
|
||||
|
||||
This read-only attribute returns the name of the queue.
|
||||
|
||||
|
||||
.. attribute:: Queue.payload_type
|
||||
|
||||
This read-only attribute returns the object type for payloads that can be
|
||||
enqueued and dequeued. If using a raw queue, this returns the value None.
|
||||
|
||||
.. versionchanged:: 8.2
|
||||
|
||||
For consistency and compliance with the PEP 8 naming style, the name of
|
||||
the attribute was changed from `payloadType`. The old name will
|
||||
continue to work for a period of time.
|
||||
|
||||
|
||||
.. _deqoptions:
|
||||
|
||||
---------------
|
||||
Dequeue Options
|
||||
---------------
|
||||
|
||||
.. note::
|
||||
|
||||
These objects are used to configure how messages are dequeued from queues.
|
||||
An instance of this object is found in the attribute
|
||||
:attr:`Queue.deqOptions`.
|
||||
|
||||
|
||||
.. attribute:: DeqOptions.condition
|
||||
|
||||
This attribute specifies a boolean expression similar to the where clause
|
||||
of a SQL query. The boolean expression can include conditions on message
|
||||
properties, user data properties and PL/SQL or SQL functions. The default
|
||||
is to have no condition specified.
|
||||
|
||||
|
||||
.. attribute:: DeqOptions.consumername
|
||||
|
||||
This attribute specifies the name of the consumer. Only messages matching
|
||||
the consumer name will be accessed. If the queue is not set up for multiple
|
||||
consumers this attribute should not be set. The default is to have no
|
||||
consumer name specified.
|
||||
|
||||
|
||||
.. attribute:: DeqOptions.correlation
|
||||
|
||||
This attribute specifies the correlation identifier of the message to be
|
||||
dequeued. Special pattern-matching characters, such as the percent sign (%)
|
||||
and the underscore (_), can be used. If multiple messages satisfy the
|
||||
pattern, the order of dequeuing is indeterminate. The default is to have no
|
||||
correlation specified.
|
||||
|
||||
|
||||
.. attribute:: DeqOptions.deliverymode
|
||||
|
||||
This write-only attribute specifies what types of messages should be
|
||||
dequeued. It should be one of the values :data:`~cx_Oracle.MSG_PERSISTENT`
|
||||
(default), :data:`~cx_Oracle.MSG_BUFFERED` or
|
||||
:data:`~cx_Oracle.MSG_PERSISTENT_OR_BUFFERED`.
|
||||
|
||||
|
||||
.. attribute:: DeqOptions.mode
|
||||
|
||||
This attribute specifies the locking behaviour associated with the dequeue
|
||||
operation. It should be one of the values :data:`~cx_Oracle.DEQ_BROWSE`,
|
||||
:data:`~cx_Oracle.DEQ_LOCKED`,
|
||||
:data:`~cx_Oracle.DEQ_REMOVE` (default), or
|
||||
:data:`~cx_Oracle.DEQ_REMOVE_NODATA`.
|
||||
|
||||
|
||||
.. attribute:: DeqOptions.msgid
|
||||
|
||||
This attribute specifies the identifier of the message to be dequeued. The
|
||||
default is to have no message identifier specified.
|
||||
|
||||
|
||||
.. attribute:: DeqOptions.navigation
|
||||
|
||||
This attribute specifies the position of the message that is retrieved. It
|
||||
should be one of the values :data:`~cx_Oracle.DEQ_FIRST_MSG`,
|
||||
:data:`~cx_Oracle.DEQ_NEXT_MSG` (default), or
|
||||
:data:`~cx_Oracle.DEQ_NEXT_TRANSACTION`.
|
||||
|
||||
|
||||
.. attribute:: DeqOptions.transformation
|
||||
|
||||
This attribute specifies the name of the transformation that must be
|
||||
applied after the message is dequeued from the database but before it is
|
||||
returned to the calling application. The transformation must be created
|
||||
using dbms_transform. The default is to have no transformation specified.
|
||||
|
||||
|
||||
.. attribute:: DeqOptions.visibility
|
||||
|
||||
This attribute specifies the transactional behavior of the dequeue request.
|
||||
It should be one of the values :data:`~cx_Oracle.DEQ_ON_COMMIT` (default)
|
||||
or :data:`~cx_Oracle.DEQ_IMMEDIATE`. This attribute is ignored when using
|
||||
the :data:`~cx_Oracle.DEQ_BROWSE` mode. Note the value of
|
||||
:attr:`~Connection.autocommit` is always ignored.
|
||||
|
||||
|
||||
.. attribute:: DeqOptions.wait
|
||||
|
||||
This attribute specifies the time to wait, in seconds, for a message
|
||||
matching the search criteria to become available for dequeuing. One of the
|
||||
values :data:`~cx_Oracle.DEQ_NO_WAIT` or
|
||||
:data:`~cx_Oracle.DEQ_WAIT_FOREVER` can also be used. The default is
|
||||
:data:`~cx_Oracle.DEQ_WAIT_FOREVER`.
|
||||
|
||||
|
||||
.. _enqoptions:
|
||||
|
||||
---------------
|
||||
Enqueue Options
|
||||
---------------
|
||||
|
||||
.. note::
|
||||
|
||||
These objects are used to configure how messages are enqueued into queues.
|
||||
An instance of this object is found in the attribute
|
||||
:attr:`Queue.enqOptions`.
|
||||
|
||||
|
||||
.. attribute:: EnqOptions.deliverymode
|
||||
|
||||
This write-only attribute specifies what type of messages should be
|
||||
enqueued. It should be one of the values :data:`~cx_Oracle.MSG_PERSISTENT`
|
||||
(default) or :data:`~cx_Oracle.MSG_BUFFERED`.
|
||||
|
||||
|
||||
.. attribute:: EnqOptions.transformation
|
||||
|
||||
This attribute specifies the name of the transformation that must be
|
||||
applied before the message is enqueued into the database. The
|
||||
transformation must be created using dbms_transform. The default is to have
|
||||
no transformation specified.
|
||||
|
||||
|
||||
.. attribute:: EnqOptions.visibility
|
||||
|
||||
This attribute specifies the transactional behavior of the enqueue request.
|
||||
It should be one of the values :data:`~cx_Oracle.ENQ_ON_COMMIT` (default)
|
||||
or :data:`~cx_Oracle.ENQ_IMMEDIATE`. Note the value of
|
||||
:attr:`~Connection.autocommit` is ignored.
|
||||
|
||||
|
||||
.. _msgproperties:
|
||||
|
||||
------------------
|
||||
Message Properties
|
||||
------------------
|
||||
|
||||
.. note::
|
||||
|
||||
These objects are used to identify the properties of messages that are
|
||||
enqueued and dequeued in queues. They are created by the method
|
||||
:meth:`Connection.msgproperties()`. They are used by the methods
|
||||
:meth:`Queue.enqone()` and :meth:`Queue.enqmany()` and
|
||||
returned by the methods :meth:`Queue.deqone()` and :meth:`Queue.deqmany()`.
|
||||
|
||||
|
||||
.. attribute:: MessageProperties.attempts
|
||||
|
||||
This read-only attribute specifies the number of attempts that have been
|
||||
made to dequeue the message.
|
||||
|
||||
|
||||
.. attribute:: MessageProperties.correlation
|
||||
|
||||
This attribute specifies the correlation used when the message was
|
||||
enqueued.
|
||||
|
||||
|
||||
.. attribute:: MessageProperties.delay
|
||||
|
||||
This attribute specifies the number of seconds to delay an enqueued
|
||||
message. Any integer is acceptable but the constant
|
||||
:data:`~cx_Oracle.MSG_NO_DELAY` can also be used indicating that the
|
||||
message is available for immediate dequeuing.
|
||||
|
||||
|
||||
.. attribute:: MessageProperties.deliverymode
|
||||
|
||||
This read-only attribute specifies the type of message that was dequeued.
|
||||
It will be one of the values :data:`~cx_Oracle.MSG_PERSISTENT` or
|
||||
:data:`~cx_Oracle.MSG_BUFFERED`.
|
||||
|
||||
|
||||
.. attribute:: MessageProperties.enqtime
|
||||
|
||||
This read-only attribute specifies the time that the message was enqueued.
|
||||
|
||||
|
||||
.. attribute:: MessageProperties.exceptionq
|
||||
|
||||
This attribute specifies the name of the queue to which the message is
|
||||
moved if it cannot be processed successfully. Messages are moved if the
|
||||
number of unsuccessful dequeue attempts has exceeded the maximum number of
|
||||
retries or if the message has expired. All messages in the exception queue
|
||||
are in the :data:`~cx_Oracle.MSG_EXPIRED` state. The default value is the
|
||||
name of the exception queue associated with the queue table.
|
||||
|
||||
|
||||
.. attribute:: MessageProperties.expiration
|
||||
|
||||
This attribute specifies, in seconds, how long the message is available for
|
||||
dequeuing. This attribute is an offset from the delay attribute. Expiration
|
||||
processing requires the queue monitor to be running. Any integer is
|
||||
accepted but the constant :data:`~cx_Oracle.MSG_NO_EXPIRATION` can also be
|
||||
used indicating that the message never expires.
|
||||
|
||||
|
||||
.. attribute:: MessageProperties.msgid
|
||||
|
||||
This read-only attribute specifies the id of the message in the last queue
|
||||
that enqueued or dequeued the message. If the message has never been
|
||||
dequeued or enqueued, the value will be `None`.
|
||||
|
||||
|
||||
.. attribute:: MessageProperties.payload
|
||||
|
||||
This attribute identifies the payload that will be enqueued or the payload
|
||||
that was dequeued when using a :ref:`queue <queue>`. When enqueuing, the
|
||||
value is checked to ensure that it conforms to the type expected by that
|
||||
queue. For RAW queues, the value can be a bytes object or a string. If the
|
||||
value is a string it will first be converted to bytes by encoding in the
|
||||
encoding identified by the attribute :attr:`Connection.encoding`.
|
||||
|
||||
|
||||
.. attribute:: MessageProperties.priority
|
||||
|
||||
This attribute specifies the priority of the message. A smaller number
|
||||
indicates a higher priority. The priority can be any integer, including
|
||||
negative numbers. The default value is zero.
|
||||
|
||||
|
||||
.. attribute:: MessageProperties.state
|
||||
|
||||
This read-only attribute specifies the state of the message at the time of
|
||||
the dequeue. It will be one of the values :data:`~cx_Oracle.MSG_WAITING`,
|
||||
:data:`~cx_Oracle.MSG_READY`, :data:`~cx_Oracle.MSG_PROCESSED` or
|
||||
:data:`~cx_Oracle.MSG_EXPIRED`.
|
|
@ -1,759 +0,0 @@
|
|||
.. _connobj:
|
||||
|
||||
*****************
|
||||
Connection Object
|
||||
*****************
|
||||
|
||||
.. note::
|
||||
|
||||
**cx_Oracle has a major new release under a new name and homepage**
|
||||
`python-oracledb <https://oracle.github.io/python-oracledb/>`__.
|
||||
|
||||
**New projects should install python-oracledb instead of cx_Oracle.**
|
||||
|
||||
.. note::
|
||||
|
||||
Any outstanding changes will be rolled back when the connection object
|
||||
is destroyed or closed.
|
||||
|
||||
|
||||
|
||||
.. method:: Connection.__enter__()
|
||||
|
||||
The entry point for the connection as a context manager. It returns itself.
|
||||
|
||||
.. note::
|
||||
|
||||
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
|
||||
the connection and roll back any uncommitted transaction.
|
||||
|
||||
.. note::
|
||||
|
||||
This method is an extension to the DB API definition.
|
||||
|
||||
|
||||
.. attribute:: Connection.action
|
||||
|
||||
This write-only attribute sets the action column in the v$session table. It
|
||||
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.
|
||||
When autocommit mode is on, all statements are committed as soon as they
|
||||
have completed executing.
|
||||
|
||||
.. note::
|
||||
|
||||
This attribute is an extension to the DB API definition.
|
||||
|
||||
|
||||
.. method:: Connection.begin([formatId, transactionId, branchId])
|
||||
|
||||
Explicitly begin a new transaction. Without parameters, this explicitly
|
||||
begins a local transaction; otherwise, this explicitly begins a distributed
|
||||
(global) transaction with the given parameters. See the Oracle
|
||||
documentation for more details.
|
||||
|
||||
Note that in order to make use of global (distributed) transactions, the
|
||||
:attr:`~Connection.internal_name` and :attr:`~Connection.external_name`
|
||||
attributes must be set.
|
||||
|
||||
.. note::
|
||||
|
||||
This method is an extension to the DB API definition.
|
||||
|
||||
|
||||
.. attribute:: Connection.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.
|
||||
|
||||
.. versionadded:: 7.0
|
||||
|
||||
.. versionchanged:: 8.2
|
||||
|
||||
For consistency and compliance with the PEP 8 naming style, the
|
||||
attribute `callTimeout` was renamed to `call_timeout`. The old name
|
||||
will continue to work for a period of time. The error *DPI-1080* was
|
||||
also introduced in this release.
|
||||
|
||||
.. note::
|
||||
|
||||
This attribute is an extension to the DB API definition and is only
|
||||
available in Oracle Client 18c and higher.
|
||||
|
||||
|
||||
.. method:: Connection.cancel()
|
||||
|
||||
Break a long-running transaction.
|
||||
|
||||
.. note::
|
||||
|
||||
This method is an extension to the DB API definition.
|
||||
|
||||
|
||||
.. method:: Connection.changepassword(oldpassword, newpassword)
|
||||
|
||||
Change the password of the logon.
|
||||
|
||||
.. note::
|
||||
|
||||
This method is an extension to the DB API definition.
|
||||
|
||||
|
||||
.. attribute:: Connection.client_identifier
|
||||
|
||||
This write-only attribute sets the client_identifier column in the
|
||||
v$session table.
|
||||
|
||||
.. note::
|
||||
|
||||
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
|
||||
table.
|
||||
|
||||
.. note::
|
||||
|
||||
This attribute is an extension to the DB API definition.
|
||||
|
||||
|
||||
.. method:: Connection.close()
|
||||
|
||||
Close the connection 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.
|
||||
|
||||
All open cursors and LOBs created by the connection will be closed and will
|
||||
also no longer be usable.
|
||||
|
||||
Internally, references to the connection are held by cursor objects,
|
||||
LOB objects, subscription objects, etc. Once all of these references are
|
||||
released, the connection itself will be closed automatically. Either
|
||||
control references to these related objects carefully or explicitly close
|
||||
connections in order to ensure sufficient resources are available.
|
||||
|
||||
|
||||
.. method:: Connection.commit()
|
||||
|
||||
Commit any pending transactions to the database.
|
||||
|
||||
|
||||
.. method:: Connection.createlob(lobType)
|
||||
|
||||
Create and return a new temporary :ref:`LOB object <lobobj>` of the
|
||||
specified type. The lobType parameter should be one of
|
||||
:data:`cx_Oracle.CLOB`, :data:`cx_Oracle.BLOB` or :data:`cx_Oracle.NCLOB`.
|
||||
|
||||
.. versionadded:: 6.2
|
||||
|
||||
.. note::
|
||||
|
||||
This method is an extension to the DB API definition.
|
||||
|
||||
|
||||
.. attribute:: Connection.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.
|
||||
|
||||
.. note::
|
||||
|
||||
This attribute is an extension to the DB API definition.
|
||||
|
||||
|
||||
.. method:: Connection.cursor()
|
||||
|
||||
Return a new :ref:`cursor object <cursorobj>` using the connection.
|
||||
|
||||
|
||||
.. 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.
|
||||
|
||||
.. note::
|
||||
|
||||
This attribute is an extension to the DB API definition.
|
||||
|
||||
|
||||
.. method:: Connection.deq(name, options, msgproperties, payload)
|
||||
|
||||
Returns a message id after successfully dequeuing a message. The options
|
||||
object can be created using :meth:`~Connection.deqoptions()` and the
|
||||
msgproperties object can be created using
|
||||
:meth:`~Connection.msgproperties()`. The payload must be an object created
|
||||
using :meth:`ObjectType.newobject()`.
|
||||
|
||||
.. versionadded:: 5.3
|
||||
|
||||
.. deprecated:: 7.2
|
||||
|
||||
Use the methods :meth:`Queue.deqone()` or :meth:`Queue.deqmany()`
|
||||
instead.
|
||||
|
||||
.. note::
|
||||
|
||||
This method is an extension to the DB API definition.
|
||||
|
||||
|
||||
.. method:: Connection.deqoptions()
|
||||
|
||||
Returns an object specifying the options to use when dequeuing messages.
|
||||
See :ref:`deqoptions` for more information.
|
||||
|
||||
.. versionadded:: 5.3
|
||||
|
||||
.. deprecated:: 7.2
|
||||
|
||||
Use the attribute :attr:`Queue.deqoptions` instead.
|
||||
|
||||
.. note::
|
||||
|
||||
This method is an extension to the DB API definition.
|
||||
|
||||
|
||||
.. attribute:: Connection.dsn
|
||||
|
||||
This read-only attribute returns the TNS entry of the database to which a
|
||||
connection has been established.
|
||||
|
||||
.. note::
|
||||
|
||||
This attribute is an extension to the DB API definition.
|
||||
|
||||
|
||||
.. attribute:: Connection.edition
|
||||
|
||||
This read-only attribute gets the session edition and is only available in
|
||||
Oracle Database 11.2 (both client and server must be at this level or
|
||||
higher for this to work).
|
||||
|
||||
.. versionadded:: 5.3
|
||||
|
||||
.. note::
|
||||
|
||||
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.
|
||||
|
||||
.. deprecated:: 8.2
|
||||
|
||||
.. note::
|
||||
|
||||
This attribute is an extension to the DB API definition.
|
||||
|
||||
|
||||
.. method:: Connection.enq(name, options, msgproperties, payload)
|
||||
|
||||
Returns a message id after successfully enqueuing a message. The options
|
||||
object can be created using :meth:`~Connection.enqoptions()` and the
|
||||
msgproperties object can be created using
|
||||
:meth:`~Connection.msgproperties()`. The payload must be an object created
|
||||
using :meth:`ObjectType.newobject()`.
|
||||
|
||||
.. versionadded:: 5.3
|
||||
|
||||
.. deprecated:: 7.2
|
||||
|
||||
Use the methods :meth:`Queue.enqone()` or :meth:`Queue.enqmany()`
|
||||
instead.
|
||||
|
||||
.. note::
|
||||
|
||||
This method is an extension to the DB API definition.
|
||||
|
||||
|
||||
.. method:: Connection.enqoptions()
|
||||
|
||||
Returns an object specifying the options to use when enqueuing messages.
|
||||
See :ref:`enqoptions` for more information.
|
||||
|
||||
.. versionadded:: 5.3
|
||||
|
||||
.. deprecated:: 7.2
|
||||
|
||||
Use the attribute :attr:`Queue.enqoptions` instead.
|
||||
|
||||
.. note::
|
||||
|
||||
This method 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
|
||||
connection when logging distributed transactions.
|
||||
|
||||
.. versionadded:: 5.3
|
||||
|
||||
.. note::
|
||||
|
||||
This attribute is an extension to the DB API definition.
|
||||
|
||||
|
||||
.. method:: Connection.getSodaDatabase()
|
||||
|
||||
Return a :ref:`SodaDatabase <sodadb>` object for Simple Oracle Document
|
||||
Access (SODA). All SODA operations are performed either on the returned
|
||||
SodaDatabase object or from objects created by the returned SodaDatabase
|
||||
object. See `here <https://www.oracle.com/pls/topic/lookup?
|
||||
ctx=dblatest&id=GUID-BE42F8D3-B86B-43B4-B2A3-5760A4DF79FB>`__ for
|
||||
additional information on SODA.
|
||||
|
||||
.. versionadded:: 7.0
|
||||
|
||||
.. note::
|
||||
|
||||
This method is an extension to the DB API definition.
|
||||
|
||||
|
||||
.. method:: Connection.gettype(name)
|
||||
|
||||
Return a :ref:`type object <objecttype>` given its name. This can then be
|
||||
used to create objects which can be bound to cursors created by this
|
||||
connection.
|
||||
|
||||
.. versionadded:: 5.3
|
||||
|
||||
.. note::
|
||||
|
||||
This method is an extension to the DB API definition.
|
||||
|
||||
|
||||
.. attribute:: Connection.handle
|
||||
|
||||
This read-only attribute returns the 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.
|
||||
|
||||
.. note::
|
||||
|
||||
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
|
||||
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.
|
||||
|
||||
.. note::
|
||||
|
||||
This attribute is an extension to the DB API definition.
|
||||
|
||||
|
||||
.. attribute:: Connection.internal_name
|
||||
|
||||
This read-write attribute specifies the internal name that is used by the
|
||||
connection when logging distributed transactions.
|
||||
|
||||
.. versionadded:: 5.3
|
||||
|
||||
.. note::
|
||||
|
||||
This attribute is an extension to the DB API definition.
|
||||
|
||||
|
||||
.. attribute:: Connection.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.
|
||||
|
||||
.. versionadded:: 5.3
|
||||
|
||||
.. note:
|
||||
|
||||
This attribute is an extension to the DB API definition. It is only
|
||||
available when Oracle Database 12.1 or higher is in use on both the
|
||||
server and the client.
|
||||
|
||||
|
||||
.. attribute:: Connection.maxBytesPerCharacter
|
||||
|
||||
This read-only attribute returns the maximum number of bytes each character
|
||||
can use for the client character set.
|
||||
|
||||
.. deprecated:: 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.
|
||||
The maximum length for this string is 48 and if you exceed this length you
|
||||
will get ORA-24960.
|
||||
|
||||
.. note:
|
||||
|
||||
This attribute is an extension to the DB API definition.
|
||||
|
||||
|
||||
.. method:: Connection.msgproperties(payload, correlation, delay, exceptionq, \
|
||||
expiration, priority)
|
||||
|
||||
Returns an object specifying the properties of messages used in advanced
|
||||
queuing. See :ref:`msgproperties` for more information.
|
||||
|
||||
Each of the parameters are optional. If specified, they act as a shortcut
|
||||
for setting each of the equivalently named properties.
|
||||
|
||||
.. versionadded:: 5.3
|
||||
|
||||
.. versionchanged:: 7.2 Added parameters
|
||||
|
||||
.. note::
|
||||
|
||||
This method 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.
|
||||
|
||||
.. deprecated:: 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, 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`.
|
||||
|
||||
.. note::
|
||||
|
||||
This attribute is an extension to the DB API definition.
|
||||
|
||||
|
||||
.. method:: Connection.ping()
|
||||
|
||||
Ping the server which can be used to test if the connection is still
|
||||
active.
|
||||
|
||||
.. note::
|
||||
|
||||
This method is an extension to the DB API definition.
|
||||
|
||||
|
||||
.. method:: Connection.prepare()
|
||||
|
||||
Prepare the distributed (global) transaction for commit. Return a boolean
|
||||
indicating if a transaction was actually prepared in order to avoid the
|
||||
error ORA-24756 (transaction does not exist).
|
||||
|
||||
.. note::
|
||||
|
||||
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
|
||||
messages in Advanced Queueing.
|
||||
|
||||
The name parameter is expected to be a string identifying the queue in
|
||||
which messages are to be enqueued or dequeued.
|
||||
|
||||
The payload_type parameter, if specified, is expected to be an
|
||||
:ref:`object type <objecttype>` that identifies the type of payload the
|
||||
queue expects. If not specified, RAW data is enqueued and dequeued.
|
||||
|
||||
.. versionadded:: 7.2
|
||||
|
||||
.. versionchanged:: 8.2
|
||||
|
||||
For consistency and compliance with the PEP 8 naming style, the
|
||||
parameter `payloadType` was renamed to `payload_type`. The old name
|
||||
will continue to work as a keyword parameter for a period of time.
|
||||
|
||||
.. note::
|
||||
|
||||
This method is an extension to the DB API definition.
|
||||
|
||||
|
||||
.. method:: Connection.rollback()
|
||||
|
||||
Rollback any pending transactions.
|
||||
|
||||
|
||||
.. method:: Connection.shutdown([mode])
|
||||
|
||||
Shutdown the database. In order to do this the connection must be connected
|
||||
as :data:`~cx_Oracle.SYSDBA` or :data:`~cx_Oracle.SYSOPER`. Two calls must
|
||||
be made unless the mode specified is :data:`~cx_Oracle.DBSHUTDOWN_ABORT`.
|
||||
An example is shown below:
|
||||
|
||||
::
|
||||
|
||||
import cx_Oracle
|
||||
|
||||
connection = cx_Oracle.connect(mode = cx_Oracle.SYSDBA)
|
||||
connection.shutdown(mode = cx_Oracle.DBSHUTDOWN_IMMEDIATE)
|
||||
cursor = connection.cursor()
|
||||
cursor.execute("alter database close normal")
|
||||
cursor.execute("alter database dismount")
|
||||
connection.shutdown(mode = cx_Oracle.DBSHUTDOWN_FINAL)
|
||||
|
||||
.. note::
|
||||
|
||||
This method is an extension to the DB API definition.
|
||||
|
||||
|
||||
.. method:: Connection.startup(force=False, restrict=False, pfile=None)
|
||||
|
||||
Startup the database. This is equivalent to the SQL\*Plus command "startup
|
||||
nomount". The connection must be connected as :data:`~cx_Oracle.SYSDBA` or
|
||||
:data:`~cx_Oracle.SYSOPER` with the :data:`~cx_Oracle.PRELIM_AUTH` option
|
||||
specified for this to work.
|
||||
|
||||
The pfile parameter, if specified, is expected to be a string identifying
|
||||
the location of the parameter file (PFILE) which will be used instead of
|
||||
the stored parameter file (SPFILE).
|
||||
|
||||
An example is shown below:
|
||||
|
||||
::
|
||||
|
||||
import cx_Oracle
|
||||
|
||||
connection = cx_Oracle.connect(
|
||||
mode=cx_Oracle.SYSDBA | cx_Oracle.PRELIM_AUTH)
|
||||
connection.startup()
|
||||
connection = cx_Oracle.connect(mode=cx_Oracle.SYSDBA)
|
||||
cursor = connection.cursor()
|
||||
cursor.execute("alter database mount")
|
||||
cursor.execute("alter database open")
|
||||
|
||||
.. note::
|
||||
|
||||
This method is an extension to the DB API definition.
|
||||
|
||||
|
||||
.. attribute:: Connection.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.
|
||||
|
||||
.. note::
|
||||
|
||||
This attribute is an extension to the DB API definition.
|
||||
|
||||
|
||||
.. method:: Connection.subscribe(namespace=cx_Oracle.SUBSCR_NAMESPACE_DBCHANGE, protocol=cx_Oracle.SUBSCR_PROTO_OCI, callback=None, timeout=0, operations=OPCODE_ALLOPS, port=0, qos=0, ip_address=None, grouping_class=0, grouping_value=0, grouping_type=cx_Oracle.SUBSCR_GROUPING_TYPE_SUMMARY, name=None, client_initiated=False)
|
||||
|
||||
Return a new :ref:`subscription object <subscrobj>` that receives
|
||||
notifications for events that take place in the database that match the
|
||||
given parameters.
|
||||
|
||||
The namespace parameter specifies the namespace the subscription uses. It
|
||||
can be one of :data:`cx_Oracle.SUBSCR_NAMESPACE_DBCHANGE` or
|
||||
:data:`cx_Oracle.SUBSCR_NAMESPACE_AQ`.
|
||||
|
||||
The protocol parameter specifies the protocol to use when notifications are
|
||||
sent. Currently the only valid value is :data:`cx_Oracle.SUBSCR_PROTO_OCI`.
|
||||
|
||||
The callback is expected to be a callable that accepts a single parameter.
|
||||
A :ref:`message object <msgobjects>` is passed to this callback whenever a
|
||||
notification is received.
|
||||
|
||||
The timeout value specifies that the subscription expires after the given
|
||||
time in seconds. The default value of 0 indicates that the subscription
|
||||
never expires.
|
||||
|
||||
The operations parameter enables filtering of the messages that are sent
|
||||
(insert, update, delete). The default value will send notifications for all
|
||||
operations. This parameter is only used when the namespace is set to
|
||||
:data:`cx_Oracle.SUBSCR_NAMESPACE_DBCHANGE`.
|
||||
|
||||
The port parameter specifies the listening port for callback notifications
|
||||
from the database server. If not specified, an unused port will be selected
|
||||
by the Oracle Client libraries.
|
||||
|
||||
The qos parameter specifies quality of service options. It should be one or
|
||||
more of the following flags, OR'ed together:
|
||||
:data:`cx_Oracle.SUBSCR_QOS_RELIABLE`,
|
||||
:data:`cx_Oracle.SUBSCR_QOS_DEREG_NFY`,
|
||||
:data:`cx_Oracle.SUBSCR_QOS_ROWIDS`,
|
||||
:data:`cx_Oracle.SUBSCR_QOS_QUERY`,
|
||||
:data:`cx_Oracle.SUBSCR_QOS_BEST_EFFORT`.
|
||||
|
||||
The ip_address parameter specifies the IP address (IPv4 or IPv6) in
|
||||
standard string notation to bind for callback notifications from the
|
||||
database server. If not specified, the client IP address will be determined
|
||||
by the Oracle Client libraries.
|
||||
|
||||
The grouping_class parameter specifies what type of grouping of
|
||||
notifications should take place. Currently, if set, this value can only be
|
||||
set to the value :data:`cx_Oracle.SUBSCR_GROUPING_CLASS_TIME`, which
|
||||
will group notifications by the number of seconds specified in the
|
||||
grouping_value parameter. The grouping_type parameter should be one of the
|
||||
values :data:`cx_Oracle.SUBSCR_GROUPING_TYPE_SUMMARY` (the default) or
|
||||
:data:`cx_Oracle.SUBSCR_GROUPING_TYPE_LAST`.
|
||||
|
||||
The name parameter is used to identify the subscription and is specific to
|
||||
the selected namespace. If the namespace parameter is
|
||||
:data:`cx_Oracle.SUBSCR_NAMESPACE_DBCHANGE` then the name is optional and
|
||||
can be any value. If the namespace parameter is
|
||||
:data:`cx_Oracle.SUBSCR_NAMESPACE_AQ`, however, the name must be in the
|
||||
format '<QUEUE_NAME>' for single consumer queues and
|
||||
'<QUEUE_NAME>:<CONSUMER_NAME>' for multiple consumer queues, and identifies
|
||||
the queue that will be monitored for messages. The queue name may include
|
||||
the schema, if needed.
|
||||
|
||||
The client_initiated parameter is used to determine if client initiated
|
||||
connections or server initiated connections (the default) will be
|
||||
established. Client initiated connections are only available in Oracle
|
||||
Client 19.4 and Oracle Database 19.4 and higher.
|
||||
|
||||
.. versionadded:: 6.4
|
||||
|
||||
The parameters ipAddress, groupingClass, groupingValue, groupingType
|
||||
and name were added.
|
||||
|
||||
.. versionadded:: 7.3
|
||||
|
||||
The parameter clientInitiated was added.
|
||||
|
||||
.. versionchanged:: 8.2
|
||||
|
||||
For consistency and compliance with the PEP 8 naming style, the
|
||||
parameter `ipAddress` was renamed to `ip_address`, the parameter
|
||||
`groupingClass` was renamed to `grouping_class`, the parameter
|
||||
`groupingValue` was renamed to `grouping_value`, the parameter
|
||||
`groupingType` was renamed to `grouping_type` and the parameter
|
||||
`clientInitiated` was renamed to `client_initiated`. The old names will
|
||||
continue to work as keyword parameters for a period of time.
|
||||
|
||||
.. note::
|
||||
|
||||
This method is an extension to the DB API definition.
|
||||
|
||||
.. note::
|
||||
|
||||
The subscription can be deregistered in the database by calling the
|
||||
function :meth:`~Connection.unsubscribe()`. If this method is not
|
||||
called and the connection that was used to create the subscription is
|
||||
explicitly closed using the function :meth:`~Connection.close()`, the
|
||||
subscription will not be deregistered in the database.
|
||||
|
||||
|
||||
.. attribute:: Connection.tag
|
||||
|
||||
This read-write attribute initially contains the actual tag of the session
|
||||
that was acquired from a pool by :meth:`SessionPool.acquire()`. If the
|
||||
connection was not acquired from a pool or no tagging parameters were
|
||||
specified (tag and matchanytag) when the connection was acquired from the
|
||||
pool, this value will be None. If the value is changed, it must be a string
|
||||
containing name=value pairs like "k1=v1;k2=v2".
|
||||
|
||||
If this value is not None when the connection is released back to the pool
|
||||
it will be used to retag the session. This value can be overridden in the
|
||||
call to :meth:`SessionPool.release()`.
|
||||
|
||||
.. note::
|
||||
|
||||
This attribute is an extension to the DB API definition.
|
||||
|
||||
.. versionadded:: 7.1
|
||||
|
||||
|
||||
.. attribute:: Connection.tnsentry
|
||||
|
||||
This read-only attribute returns the TNS entry of the database to which a
|
||||
connection has been established.
|
||||
|
||||
.. deprecated:: 8.2
|
||||
|
||||
Use the attribute :attr:`~Connection.dsn` instead.
|
||||
|
||||
.. note::
|
||||
|
||||
This attribute is an extension to the DB API definition.
|
||||
|
||||
|
||||
.. method:: Connection.unsubscribe(subscr)
|
||||
|
||||
Unsubscribe from events in the database that were originally subscribed to
|
||||
using :meth:`~Connection.subscribe()`. The connection used to unsubscribe
|
||||
should be the same one used to create the subscription, or should access
|
||||
the same database and be connected as the same user name.
|
||||
|
||||
.. versionadded:: 6.4
|
||||
|
||||
|
||||
.. attribute:: Connection.username
|
||||
|
||||
This read-only attribute returns the name of the user which established the
|
||||
connection to the database.
|
||||
|
||||
.. note::
|
||||
|
||||
This attribute is an extension to the DB API definition.
|
||||
|
||||
|
||||
.. attribute:: Connection.version
|
||||
|
||||
This read-only attribute returns the version of the database to which a
|
||||
connection has been established.
|
||||
|
||||
.. note::
|
||||
|
||||
This attribute is an extension to the DB API definition.
|
||||
|
||||
.. note::
|
||||
|
||||
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).
|
|
@ -1,678 +0,0 @@
|
|||
.. _cursorobj:
|
||||
|
||||
*************
|
||||
Cursor Object
|
||||
*************
|
||||
|
||||
|
||||
.. note::
|
||||
|
||||
**cx_Oracle has a major new release under a new name and homepage**
|
||||
`python-oracledb <https://oracle.github.io/python-oracledb/>`__.
|
||||
|
||||
**New projects should install python-oracledb instead of cx_Oracle.**
|
||||
|
||||
.. method:: Cursor.__enter__()
|
||||
|
||||
The entry point for the cursor as a context manager. It returns itself.
|
||||
|
||||
.. note::
|
||||
|
||||
This method is an extension to the DB API definition.
|
||||
|
||||
|
||||
.. method:: Cursor.__exit__()
|
||||
|
||||
The exit point for the cursor as a context manager. It closes the cursor.
|
||||
|
||||
.. note::
|
||||
|
||||
This method is an extension to the DB API definition.
|
||||
|
||||
|
||||
.. attribute:: Cursor.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:`~Cursor.fetchone()` and :meth:`~Cursor.fetchall()` it does not change
|
||||
how many rows are returned to the application. For
|
||||
:meth:`~Cursor.fetchmany()` it is the default number of rows to fetch.
|
||||
|
||||
Due to the performance benefits, the default ``Cursor.arraysize`` is 100
|
||||
instead of the 1 that the DB API recommends. This value means that 100 rows
|
||||
are fetched by each internal call to the database.
|
||||
|
||||
See :ref:`Tuning Fetch Performance <tuningfetch>` for more information.
|
||||
|
||||
.. attribute:: Cursor.bindarraysize
|
||||
|
||||
This read-write attribute specifies the number of rows to bind at a time
|
||||
and is used when creating variables via :meth:`~Cursor.setinputsizes()` or
|
||||
:meth:`~Cursor.var()`. It defaults to 1 meaning to bind a single row at a
|
||||
time.
|
||||
|
||||
.. note::
|
||||
|
||||
The DB API definition does not define this attribute.
|
||||
|
||||
|
||||
.. method:: Cursor.arrayvar(typ, value, [size])
|
||||
|
||||
Create an array variable associated with the cursor of the given type and
|
||||
size and return 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-cx_Oracle/blob/main/
|
||||
samples/plsql_collection.py>`__ needs to be used.
|
||||
|
||||
.. note::
|
||||
|
||||
The DB API definition does not define this method.
|
||||
|
||||
|
||||
.. method:: Cursor.bindnames()
|
||||
|
||||
Return the list of bind variable names bound to the statement. Note that a
|
||||
statement must have been prepared first.
|
||||
|
||||
.. note::
|
||||
|
||||
The DB API definition does not define this method.
|
||||
|
||||
|
||||
.. attribute:: Cursor.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.
|
||||
|
||||
.. note::
|
||||
|
||||
The DB API definition does not define this attribute.
|
||||
|
||||
|
||||
.. method:: Cursor.callfunc(name, returnType, parameters=[], \
|
||||
keyword_parameters={})
|
||||
|
||||
Call a function with the given name. The return type is specified in the
|
||||
same notation as is required by :meth:`~Cursor.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.
|
||||
|
||||
.. versionchanged:: 8.2
|
||||
|
||||
For consistency and compliance with the PEP 8 naming style, the
|
||||
parameter `keywordParameters` was renamed to `keyword_parameters`. The
|
||||
old name will continue to work as a keyword parameter for a period of
|
||||
time.
|
||||
|
||||
.. note::
|
||||
|
||||
The DB API definition does not define this method.
|
||||
|
||||
.. note::
|
||||
|
||||
If you intend to call :meth:`Cursor.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:: Cursor.callproc(name, parameters=[], keyword_parameters={})
|
||||
|
||||
Call 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.
|
||||
|
||||
.. versionchanged:: 8.2
|
||||
|
||||
For consistency and compliance with the PEP 8 naming style, the
|
||||
parameter `keywordParameters` was renamed to `keyword_parameters`. The
|
||||
old name will continue to work as a keyword parameter for a period of
|
||||
time.
|
||||
|
||||
.. note::
|
||||
|
||||
The DB API definition does not allow for keyword parameters.
|
||||
|
||||
|
||||
.. method:: Cursor.close()
|
||||
|
||||
Close 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.
|
||||
|
||||
|
||||
.. attribute:: Cursor.connection
|
||||
|
||||
This read-only attribute returns a reference to the connection object on
|
||||
which the cursor was created.
|
||||
|
||||
.. note::
|
||||
|
||||
This attribute is an extension to the DB API definition but it is
|
||||
mentioned in PEP 249 as an optional extension.
|
||||
|
||||
|
||||
.. data:: Cursor.description
|
||||
|
||||
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.
|
||||
|
||||
The type will be one of the :ref:`database type constants <dbtypes>`
|
||||
defined at the module level.
|
||||
|
||||
|
||||
.. method:: Cursor.execute(statement, parameters=[], ** keyword_parameters)
|
||||
|
||||
Execute 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 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
|
||||
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.
|
||||
|
||||
.. note::
|
||||
|
||||
The DB API definition does not define the return value of this method.
|
||||
|
||||
|
||||
.. method:: Cursor.executemany(statement, parameters, batcherrors=False, \
|
||||
arraydmlrowcounts=False)
|
||||
|
||||
Prepare 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 is managed in the same way as the :meth:`~Cursor.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:`~Cursor.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:`~Cursor.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:`~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
|
||||
a TypeError exception.
|
||||
|
||||
|
||||
.. method:: Cursor.executemanyprepared(num_iters)
|
||||
|
||||
Execute the previously prepared and bound statement the given number of
|
||||
times. The variables that are bound must have already been set to their
|
||||
desired value before this call is made. This method was designed for the
|
||||
case where optimal performance is required as it comes at the expense of
|
||||
compatibility with the DB API.
|
||||
|
||||
.. note::
|
||||
|
||||
The DB API definition does not define this method.
|
||||
|
||||
.. deprecated:: 6.4
|
||||
Use :meth:`~Cursor.executemany()` instead with None for the statement
|
||||
argument and an integer for the parameters argument.
|
||||
|
||||
|
||||
.. method:: Cursor.fetchall()
|
||||
|
||||
Fetch 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 the arraysize.
|
||||
|
||||
An exception is raised if the previous call to :meth:`~Cursor.execute()`
|
||||
did not produce any result set or no call was issued yet.
|
||||
|
||||
See :ref:`fetching` for an example.
|
||||
|
||||
|
||||
.. method:: Cursor.fetchmany(numRows=cursor.arraysize)
|
||||
|
||||
Fetch 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:`~Cursor.execute()`
|
||||
did not produce any result set or no call was issued yet.
|
||||
|
||||
See :ref:`fetching` for an example.
|
||||
|
||||
.. method:: Cursor.fetchone()
|
||||
|
||||
Fetch 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:`~Cursor.execute()`
|
||||
did not produce any result set or no call was issued yet.
|
||||
|
||||
See :ref:`fetching` for an example.
|
||||
|
||||
.. method:: Cursor.fetchraw(num_rows=cursor.arraysize)
|
||||
|
||||
Fetch the next set of rows of a query result into the internal buffers of
|
||||
the defined variables for the cursor. The number of rows actually fetched
|
||||
is returned.
|
||||
|
||||
An exception is raised if the previous call to :meth:`~Cursor.execute()`
|
||||
did not produce any result set or no call was issued yet.
|
||||
|
||||
.. deprecated:: 8.2
|
||||
|
||||
Use :meth:`Cursor.fetchmany()` instead.
|
||||
|
||||
.. note::
|
||||
|
||||
The DB API definition does not define this method.
|
||||
|
||||
|
||||
.. attribute:: Cursor.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.
|
||||
|
||||
.. note::
|
||||
|
||||
The DB API definition does not define this attribute.
|
||||
|
||||
|
||||
.. method:: Cursor.getarraydmlrowcounts()
|
||||
|
||||
Retrieve the DML row counts after a call to :meth:`~Cursor.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:`~Cursor.executemany()`.
|
||||
|
||||
.. note::
|
||||
|
||||
The DB API definition does not define this method and it is only
|
||||
available for Oracle 12.1 and higher.
|
||||
|
||||
|
||||
.. method:: Cursor.getbatcherrors()
|
||||
|
||||
Retrieve the exceptions that took place after a call to
|
||||
:meth:`~Cursor.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.
|
||||
|
||||
.. note::
|
||||
|
||||
The DB API definition does not define this method.
|
||||
|
||||
|
||||
.. method:: Cursor.getimplicitresults()
|
||||
|
||||
Return 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 client 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.
|
||||
|
||||
.. versionadded:: 5.3
|
||||
|
||||
.. note::
|
||||
|
||||
The DB API definition does not define this method and it is only
|
||||
available for Oracle Database 12.1 (both client and server must be at
|
||||
this level or higher). 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.
|
||||
|
||||
|
||||
.. attribute:: Cursor.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 value of the attribute with the
|
||||
same name on the connection is used.
|
||||
|
||||
.. note::
|
||||
|
||||
This attribute 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.
|
||||
|
||||
|
||||
.. 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.
|
||||
|
||||
.. versionadded:: 7.3
|
||||
|
||||
|
||||
.. 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, 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 value of
|
||||
the attribute with the same name on the connection is used instead.
|
||||
|
||||
See :ref:`outputtypehandlers`.
|
||||
|
||||
.. note::
|
||||
|
||||
This attribute is an extension to the DB API definition.
|
||||
|
||||
|
||||
.. method:: Cursor.parse(statement)
|
||||
|
||||
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::
|
||||
|
||||
The DB API definition does not define this method.
|
||||
|
||||
.. note::
|
||||
|
||||
You can parse any DML or DDL statement. DDL statements are executed
|
||||
immediately and an implied commit takes place.
|
||||
|
||||
|
||||
.. attribute:: Cursor.prefetchrows
|
||||
|
||||
This read-write attribute can be used to tune the number of rows that the
|
||||
Oracle Client library 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.
|
||||
|
||||
See :ref:`Tuning Fetch Performance <tuningfetch>` for more information.
|
||||
|
||||
.. note::
|
||||
|
||||
The DB API definition does not define this method.
|
||||
|
||||
|
||||
.. method:: Cursor.prepare(statement, [tag])
|
||||
|
||||
This can be used before a call to :meth:`~Cursor.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:`~Cursor.execute()` is made with
|
||||
None or the same string object as the statement. If specified the
|
||||
statement will be returned to the statement cache with the given tag. See
|
||||
the Oracle documentation for more information about the statement cache.
|
||||
|
||||
See :ref:`Statement Caching <stmtcache>` for more information.
|
||||
|
||||
.. note::
|
||||
|
||||
The DB API definition does not define this method.
|
||||
|
||||
|
||||
.. attribute:: Cursor.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:: Cursor.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`.
|
||||
|
||||
.. note::
|
||||
|
||||
The DB API definition does not define this attribute.
|
||||
|
||||
|
||||
.. method:: Cursor.scroll(value=0, mode="relative")
|
||||
|
||||
Scroll the cursor in the result set to a new position according to the
|
||||
mode.
|
||||
|
||||
If mode is "relative" (the default value), the value is taken as an offset
|
||||
to the current position in the result set. If set to "absolute", value
|
||||
states an absolute target position. If set to "first", the cursor is
|
||||
positioned at the first row and if set to "last", the cursor is set to the
|
||||
last row in the result set.
|
||||
|
||||
An error is raised if the mode is "relative" or "absolute" and the scroll
|
||||
operation would position the cursor outside of the result set.
|
||||
|
||||
.. versionadded:: 5.3
|
||||
|
||||
.. note::
|
||||
|
||||
This method is an extension to the DB API definition but it is
|
||||
mentioned in PEP 249 as an optional extension.
|
||||
|
||||
|
||||
.. attribute:: Cursor.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:`~Cursor.execute()`.
|
||||
|
||||
.. versionadded:: 5.3
|
||||
|
||||
.. note::
|
||||
|
||||
The DB API definition does not define this attribute.
|
||||
|
||||
|
||||
.. method:: Cursor.setinputsizes(\*args, \*\*keywordArgs)
|
||||
|
||||
This can be used before a call to :meth:`~Cursor.execute()`,
|
||||
: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).
|
||||
|
||||
|
||||
.. attribute:: Cursor.statement
|
||||
|
||||
This read-only attribute provides the string object that was previously
|
||||
prepared with :meth:`~Cursor.prepare()` or executed with
|
||||
:meth:`~Cursor.execute()`.
|
||||
|
||||
.. note::
|
||||
|
||||
The DB API definition does not define this attribute.
|
||||
|
||||
|
||||
.. method:: Cursor.var(typ, [size, arraysize, inconverter, outconverter, \
|
||||
typename, encoding_errors, bypass_decode])
|
||||
|
||||
Create 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:`Connection.gettype()` or one of the following Python
|
||||
types:
|
||||
|
||||
.. list-table::
|
||||
:header-rows: 1
|
||||
|
||||
* - Python Type
|
||||
- Database Type
|
||||
* - bool
|
||||
- :attr:`cx_Oracle.DB_TYPE_BOOLEAN`
|
||||
* - bytes
|
||||
- :attr:`cx_Oracle.DB_TYPE_RAW`
|
||||
* - datetime.date
|
||||
- :attr:`cx_Oracle.DB_TYPE_DATE`
|
||||
* - datetime.datetime
|
||||
- :attr:`cx_Oracle.DB_TYPE_DATE`
|
||||
* - datetime.timedelta
|
||||
- :attr:`cx_Oracle.DB_TYPE_INTERVAL_DS`
|
||||
* - decimal.Decimal
|
||||
- :attr:`cx_Oracle.DB_TYPE_NUMBER`
|
||||
* - float
|
||||
- :attr:`cx_Oracle.DB_TYPE_NUMBER`
|
||||
* - int
|
||||
- :attr:`cx_Oracle.DB_TYPE_NUMBER`
|
||||
* - str
|
||||
- :attr:`cx_Oracle.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:`cx_Oracle.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:`~cx_Oracle.DB_TYPE_VARCHAR`, :data:`~cx_Oracle.DB_TYPE_CHAR`,
|
||||
:data:`~cx_Oracle.DB_TYPE_NVARCHAR`, :data:`~cx_Oracle.DB_TYPE_NCHAR` and
|
||||
:data:`~cx_Oracle.DB_TYPE_LONG` to be returned as `bytes` instead of `str`,
|
||||
meaning that cx_Oracle doesn't do any decoding. See :ref:`Fetching raw
|
||||
data <fetching-raw-data>` for more information.
|
||||
|
||||
.. versionadded:: 8.2
|
||||
|
||||
The parameter `bypass_decode` was added.
|
||||
|
||||
.. versionchanged:: 8.2
|
||||
|
||||
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.
|
||||
|
||||
.. note::
|
||||
|
||||
The DB API definition does not define this method.
|
|
@ -1,184 +0,0 @@
|
|||
.. _deprecations:
|
||||
|
||||
************
|
||||
Deprecations
|
||||
************
|
||||
|
||||
.. note::
|
||||
|
||||
**cx_Oracle has a major new release under a new name and homepage**
|
||||
`python-oracledb <https://oracle.github.io/python-oracledb/>`__.
|
||||
|
||||
**New projects should install python-oracledb instead of cx_Oracle.**
|
||||
|
||||
The following tables contains all of the deprecations in the cx_Oracle 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:: Deprecated in 8.2
|
||||
:header-rows: 1
|
||||
:widths: 25 75
|
||||
:width: 100%
|
||||
:name: _deprecations_8_2
|
||||
|
||||
* - Name
|
||||
- Comments
|
||||
* - `encoding` parameter to :meth:`cx_Oracle.connect()`
|
||||
- No longer needed as the use of encodings other than UTF-8 is
|
||||
deprecated. Encoding is handled internally between cx_Oracle and Oracle
|
||||
Database.
|
||||
* - `nencoding` parameter to :meth:`cx_Oracle.connect()`
|
||||
- No longer needed as the use of encodings other than UTF-8 is
|
||||
deprecated.
|
||||
* - `encoding` parameter to :meth:`cx_Oracle.SessionPool()`
|
||||
- No longer needed as the use of encodings other than UTF-8 is
|
||||
deprecated.
|
||||
* - `nencoding` parameter to :meth:`cx_Oracle.SessionPool()`
|
||||
- No longer needed as the use of encodings other than UTF-8 is
|
||||
deprecated.
|
||||
* - Connection.maxBytesPerCharacter
|
||||
- No longer needed as the use of encodings other than UTF-8 is
|
||||
deprecated. The constant value 4 can be used instead.
|
||||
* - Positional parameters to :meth:`cx_Oracle.connect()`
|
||||
- Replace with keyword parameters in order to comply with the Python
|
||||
database API.
|
||||
* - Positional parameters to :meth:`cx_Oracle.SessionPool()`
|
||||
- Replace with keyword parameters in order to comply with the Python
|
||||
database API.
|
||||
* - `threaded` parameter to :meth:`cx_Oracle.SessionPool()`
|
||||
- The value of this parameter is ignored. Threading is now always used.
|
||||
* - `waitTimeout` parameter to :meth:`cx_Oracle.SessionPool()`
|
||||
- Replace with parameter name `wait_timeout`
|
||||
* - `maxLifetimeSession` parameter to :meth:`cx_Oracle.SessionPool()`
|
||||
- Replace with parameter name `max_lifetime_session`
|
||||
* - `sessionCallback` parameter to :meth:`cx_Oracle.SessionPool()`
|
||||
- Replace with parameter name `session_callback`
|
||||
* - `maxSessionsPerShard` parameter to :meth:`cx_Oracle.SessionPool()`
|
||||
- Replace with parameter name `max_sessions_per_shard`
|
||||
* - `SessionPool.tnsentry`
|
||||
- Replace with :data:`SessionPool.dsn`
|
||||
* - `payloadType` parameter to :meth:`Connection.queue()`
|
||||
- Replace with parameter name `payload_type` if using keyword parameters.
|
||||
* - `ipAddress` parameter to :meth:`Connection.subscribe()`
|
||||
- Replace with parameter name `ip_address`
|
||||
* - `groupingClass` parameter to :meth:`Connection.subscribe()`
|
||||
- Replace with parameter name `grouping_class`
|
||||
* - `groupingValue` parameter to :meth:`Connection.subscribe()`
|
||||
- Replace with parameter name `grouping_value`
|
||||
* - `groupingType` parameter to :meth:`Connection.subscribe()`
|
||||
- Replace with parameter name `grouping_type`
|
||||
* - `clientInitiated` parameter to :meth:`Connection.subscribe()`
|
||||
- Replace with parameter name `client_initiated`
|
||||
* - `Connection.callTimeout`
|
||||
- Replace with :data:`Connection.call_timeout`
|
||||
* - `Connection.tnsentry`
|
||||
- Replace with :data:`Connection.dsn`
|
||||
* - `keywordParameters` parameter to :meth:`Cursor.callfunc()`
|
||||
- Replace with parameter name `keyword_parameters`
|
||||
* - `keywordParameters` parameter to :meth:`Cursor.callproc()`
|
||||
- Replace with parameter name `keyword_parameters`
|
||||
* - `encodingErrors` parameter to :meth:`Cursor.var()`
|
||||
- Replace with parameter name `encoding_errors`
|
||||
* - `Cursor.fetchraw()`
|
||||
- Replace with :meth:`Cursor.fetchmany()`
|
||||
* - `newSize` parameter to :meth:`LOB.trim()`
|
||||
- Replace with parameter name `new_size`
|
||||
* - `Queue.deqMany`
|
||||
- Replace with :meth:`Queue.deqmany()`
|
||||
* - `Queue.deqOne`
|
||||
- Replace with :meth:`Queue.deqone()`
|
||||
* - `Queue.enqMany`
|
||||
- Replace with :meth:`Queue.enqmany()`
|
||||
* - `Queue.enqOne`
|
||||
- Replace with :meth:`Queue.enqone()`
|
||||
* - `Queue.deqOptions`
|
||||
- Replace with :data:`Queue.deqoptions`
|
||||
* - `Queue.enqOptions`
|
||||
- Replace with :meth:`Queue.enqoptions`
|
||||
* - `Queue.payloadType`
|
||||
- Replace with :meth:`Queue.payload_type`
|
||||
* - `Subscription.ipAddress`
|
||||
- Replace with :attr:`Subscription.ip_address`
|
||||
* - `Message.consumerName`
|
||||
- Replace with :attr:`Message.consumer_name`
|
||||
* - `Message.queueName`
|
||||
- Replace with :attr:`Message.queue_name`
|
||||
* - `Variable.actualElements`
|
||||
- Replace with :attr:`Variable.actual_elements`
|
||||
* - `Variable.bufferSize`
|
||||
- Replace with :attr:`Variable.buffer_size`
|
||||
* - `Variable.numElements`
|
||||
- Replace with :attr:`Variable.num_elements`
|
||||
|
||||
|
||||
.. list-table:: Deprecated in 8.0
|
||||
:header-rows: 1
|
||||
:widths: 25 75
|
||||
:width: 100%
|
||||
:name: _deprecations_8_0
|
||||
|
||||
* - Name
|
||||
- Comments
|
||||
* - cx_Oracle.BFILE
|
||||
- Replace with :data:`cx_Oracle.DB_TYPE_BFILE`
|
||||
* - cx_Oracle.BLOB
|
||||
- Replace with :data:`cx_Oracle.DB_TYPE_BLOB`
|
||||
* - cx_Oracle.BOOLEAN
|
||||
- Replace with :data:`cx_Oracle.DB_TYPE_BOOLEAN`
|
||||
* - cx_Oracle.CLOB
|
||||
- Replace with :data:`cx_Oracle.DB_TYPE_CLOB`
|
||||
* - cx_Oracle.CURSOR
|
||||
- Replace with :data:`cx_Oracle.DB_TYPE_CURSOR`
|
||||
* - cx_Oracle.FIXED_CHAR
|
||||
- Replace with :data:`cx_Oracle.DB_TYPE_CHAR`
|
||||
* - cx_Oracle.FIXED_NCHAR
|
||||
- Replace with :data:`cx_Oracle.DB_TYPE_NCHAR`
|
||||
* - cx_Oracle.INTERVAL
|
||||
- Replace with :data:`cx_Oracle.DB_TYPE_INTERVAL_DS`
|
||||
* - cx_Oracle.LONG_BINARY
|
||||
- Replace with :data:`cx_Oracle.DB_TYPE_LONG_RAW`
|
||||
* - cx_Oracle.LONG_STRING
|
||||
- Replace with :data:`cx_Oracle.DB_TYPE_LONG`
|
||||
* - cx_Oracle.NATIVE_FLOAT
|
||||
- Replace with :data:`cx_Oracle.DB_TYPE_BINARY_DOUBLE`
|
||||
* - cx_Oracle.NATIVE_INT
|
||||
- Replace with :data:`cx_Oracle.DB_TYPE_BINARY_INTEGER`
|
||||
* - cx_Oracle.NCHAR
|
||||
- Replace with :data:`cx_Oracle.DB_TYPE_NVARCHAR`
|
||||
* - cx_Oracle.NCLOB
|
||||
- Replace with :data:`cx_Oracle.DB_TYPE_NCLOB`
|
||||
* - cx_Oracle.OBJECT
|
||||
- Replace with :data:`cx_Oracle.DB_TYPE_OBJECT`
|
||||
* - cx_Oracle.TIMESTAMP
|
||||
- Replace with :data:`cx_Oracle.DB_TYPE_TIMESTAMP`
|
||||
|
||||
|
||||
.. list-table:: Deprecated in 7.2
|
||||
:header-rows: 1
|
||||
:widths: 25 75
|
||||
:width: 100%
|
||||
:name: _deprecations_7_2
|
||||
|
||||
* - Name
|
||||
- Comments
|
||||
* - Connection.deq()
|
||||
- Replace with :meth:`Queue.deqone()` or :meth:`Queue.deqmany()`.
|
||||
* - Connection.deqoptions()
|
||||
- Replace with attribute :attr:`Queue.deqoptions`.
|
||||
* - Connection.enq()
|
||||
- Replace with :meth:`Queue.enqone()` or :meth:`Queue.enqmany()`.
|
||||
* - Connection.enqoptions()
|
||||
- Replace with attribute :attr:`Queue.enqoptions`.
|
||||
|
||||
|
||||
.. list-table:: Deprecated in 6.4
|
||||
:header-rows: 1
|
||||
:widths: 25 75
|
||||
:width: 100%
|
||||
:name: _deprecations_6_4
|
||||
|
||||
* - Name
|
||||
- Comments
|
||||
* - Cursor.executemanyprepared()
|
||||
- Replace with :meth:`~Cursor.executemany()` with None for the statement
|
||||
argument and an integer for the parameters argument.
|
|
@ -1,105 +0,0 @@
|
|||
.. _lobobj:
|
||||
|
||||
***********
|
||||
LOB Objects
|
||||
***********
|
||||
|
||||
.. note::
|
||||
|
||||
**cx_Oracle has a major new release under a new name and homepage**
|
||||
`python-oracledb <https://oracle.github.io/python-oracledb/>`__.
|
||||
|
||||
**New projects should install python-oracledb instead of cx_Oracle.**
|
||||
|
||||
See :ref:`lobdata` for more information about using LOBs.
|
||||
|
||||
.. note::
|
||||
|
||||
This object is an extension the DB API. It is returned whenever Oracle
|
||||
:data:`CLOB`, :data:`BLOB` and :data:`BFILE` columns are fetched.
|
||||
|
||||
|
||||
.. method:: LOB.close()
|
||||
|
||||
Close the LOB. Call this when writing is completed so that the indexes
|
||||
associated with the LOB can be updated -- but only if :meth:`~LOB.open()`
|
||||
was called first.
|
||||
|
||||
|
||||
.. method:: LOB.fileexists()
|
||||
|
||||
Return a boolean indicating if the file referenced by the BFILE type LOB
|
||||
exists.
|
||||
|
||||
|
||||
.. method:: LOB.getchunksize()
|
||||
|
||||
Return 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()
|
||||
|
||||
Return a two-tuple consisting of the directory alias and file name for a
|
||||
BFILE type LOB.
|
||||
|
||||
|
||||
.. method:: LOB.isopen()
|
||||
|
||||
Return a boolean indicating if the LOB has been opened using the method
|
||||
:meth:`~LOB.open()`.
|
||||
|
||||
|
||||
.. method:: LOB.open()
|
||||
|
||||
Open 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:: LOB.read([offset=1, [amount]])
|
||||
|
||||
Return 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:: LOB.setfilename(dirAlias, name)
|
||||
|
||||
Set 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
|
||||
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:: LOB.trim(new_size=0)
|
||||
|
||||
Trim the LOB to the new size.
|
||||
|
||||
|
||||
.. attribute:: LOB.type
|
||||
|
||||
This read-only attribute returns the type of the LOB as one of the
|
||||
:ref:`database type constants <dbtypes>`.
|
||||
|
||||
.. versionadded:: 8.0
|
||||
|
||||
|
||||
.. method:: LOB.write(data, offset=1)
|
||||
|
||||
Write 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:`~LOB.trim()` function.
|
File diff suppressed because it is too large
Load Diff
|
@ -1,197 +0,0 @@
|
|||
.. _objecttype:
|
||||
|
||||
*******************
|
||||
Object Type Objects
|
||||
*******************
|
||||
|
||||
.. note::
|
||||
|
||||
**cx_Oracle has a major new release under a new name and homepage**
|
||||
`python-oracledb <https://oracle.github.io/python-oracledb/>`__.
|
||||
|
||||
**New projects should install python-oracledb instead of cx_Oracle.**
|
||||
|
||||
.. note::
|
||||
|
||||
This object is an extension to the DB API. It is returned by the
|
||||
:meth:`Connection.gettype()` call and is available as the
|
||||
:data:`Variable.type` for variables containing Oracle objects.
|
||||
|
||||
|
||||
.. method:: ObjectType([sequence])
|
||||
|
||||
The object type may be called directly and serves as an alternative way of
|
||||
calling :meth:`~ObjectType.newobject()`.
|
||||
|
||||
|
||||
.. attribute:: ObjectType.attributes
|
||||
|
||||
This read-only attribute returns a list of the :ref:`attributes
|
||||
<objectattr>` that make up the object type.
|
||||
|
||||
|
||||
.. attribute:: ObjectType.iscollection
|
||||
|
||||
This read-only attribute returns a boolean indicating if the object type
|
||||
refers to a collection or not.
|
||||
|
||||
|
||||
.. attribute:: ObjectType.name
|
||||
|
||||
This read-only attribute returns the name of the type.
|
||||
|
||||
|
||||
.. attribute:: ObjectType.element_type
|
||||
|
||||
This read-only attribute returns the type of elements found in collections
|
||||
of this type, if :attr:`~ObjectType.iscollection` is ``True``; otherwise,
|
||||
it returns ``None``. If the collection contains objects, this will be
|
||||
another object type; otherwise, it will be one of the
|
||||
:ref:`database type constants <dbtypes>`.
|
||||
|
||||
.. versionadded:: 8.0
|
||||
|
||||
|
||||
.. method:: ObjectType.newobject([sequence])
|
||||
|
||||
Return a new Oracle object of the given type. This object can then be
|
||||
modified by setting its attributes and then bound to a cursor for
|
||||
interaction with Oracle. If the object type refers to a collection, a
|
||||
sequence may be passed and the collection will be initialized with the
|
||||
items in that sequence.
|
||||
|
||||
|
||||
.. attribute:: ObjectType.schema
|
||||
|
||||
This read-only attribute returns the name of the schema that owns the type.
|
||||
|
||||
|
||||
Object Objects
|
||||
--------------
|
||||
|
||||
.. note::
|
||||
|
||||
This object is an extension to the DB API. It is returned by the
|
||||
:meth:`ObjectType.newobject()` call and can be bound to variables of
|
||||
type :data:`~cx_Oracle.OBJECT`. Attributes can be retrieved and set
|
||||
directly.
|
||||
|
||||
.. method:: Object.append(element)
|
||||
|
||||
Append an element to the collection object. If no elements exist in the
|
||||
collection, this creates an element at index 0; otherwise, it creates an
|
||||
element immediately following the highest index available in the
|
||||
collection.
|
||||
|
||||
|
||||
.. method:: Object.asdict()
|
||||
|
||||
Return a dictionary where the collection's indexes are the keys and the
|
||||
elements are its values.
|
||||
|
||||
.. versionadded:: 7.0
|
||||
|
||||
|
||||
.. method:: Object.aslist()
|
||||
|
||||
Return a list of each of the collection's elements in index order.
|
||||
|
||||
|
||||
.. method:: Object.copy()
|
||||
|
||||
Create a copy of the object and return it.
|
||||
|
||||
|
||||
.. method:: Object.delete(index)
|
||||
|
||||
Delete the element at the specified index of the collection. If the
|
||||
element does not exist or is otherwise invalid, an error is raised. Note
|
||||
that the indices of the remaining elements in the collection are not
|
||||
changed. In other words, the delete operation creates holes in the
|
||||
collection.
|
||||
|
||||
|
||||
.. method:: Object.exists(index)
|
||||
|
||||
Return True or False indicating if an element exists in the collection at
|
||||
the specified index.
|
||||
|
||||
|
||||
.. method:: Object.extend(sequence)
|
||||
|
||||
Append all of the elements in the sequence to the collection. This is
|
||||
the equivalent of performing :meth:`~Object.append()` for each element
|
||||
found in the sequence.
|
||||
|
||||
|
||||
.. method:: Object.first()
|
||||
|
||||
Return the index of the first element in the collection. If the collection
|
||||
is empty, None is returned.
|
||||
|
||||
|
||||
.. method:: Object.getelement(index)
|
||||
|
||||
Return the element at the specified index of the collection. If no element
|
||||
exists at that index, an exception is raised.
|
||||
|
||||
|
||||
.. method:: Object.last()
|
||||
|
||||
Return the index of the last element in the collection. If the collection
|
||||
is empty, None is returned.
|
||||
|
||||
|
||||
.. method:: Object.next(index)
|
||||
|
||||
Return the index of the next element in the collection following the
|
||||
specified index. If there are no elements in the collection following the
|
||||
specified index, None is returned.
|
||||
|
||||
|
||||
.. method:: Object.prev(index)
|
||||
|
||||
Return the index of the element in the collection preceding the specified
|
||||
index. If there are no elements in the collection preceding the
|
||||
specified index, None is returned.
|
||||
|
||||
|
||||
.. method:: Object.setelement(index, value)
|
||||
|
||||
Set the value in the collection at the specified index to the given value.
|
||||
|
||||
|
||||
.. method:: Object.size()
|
||||
|
||||
Return the number of elements in the collection.
|
||||
|
||||
|
||||
.. method:: Object.trim(num)
|
||||
|
||||
Remove the specified number of elements from the end of the collection.
|
||||
|
||||
|
||||
.. _objectattr:
|
||||
|
||||
Object Attribute Objects
|
||||
------------------------
|
||||
|
||||
.. note::
|
||||
|
||||
This object is an extension to the DB API. The elements of
|
||||
:attr:`ObjectType.attributes` are instances of this type.
|
||||
|
||||
|
||||
.. attribute:: ObjectAttribute.name
|
||||
|
||||
This read-only attribute returns the name of the attribute.
|
||||
|
||||
|
||||
.. attribute:: ObjectAttribute.type
|
||||
|
||||
This read-only attribute returns the type of the attribute. This will be an
|
||||
:ref:`Oracle Object Type <objecttype>` if the variable binds
|
||||
Oracle objects; otherwise, it will be one of the
|
||||
:ref:`database type constants <dbtypes>`.
|
||||
|
||||
.. versionadded:: 8.0
|
|
@ -1,309 +0,0 @@
|
|||
.. _sesspool:
|
||||
|
||||
******************
|
||||
SessionPool Object
|
||||
******************
|
||||
|
||||
.. note::
|
||||
|
||||
**cx_Oracle has a major new release under a new name and homepage**
|
||||
`python-oracledb <https://oracle.github.io/python-oracledb/>`__.
|
||||
|
||||
**New projects should install python-oracledb instead of cx_Oracle.**
|
||||
|
||||
.. note::
|
||||
|
||||
This object is an extension to the DB API.
|
||||
|
||||
Connection pooling in cx_Oracle is handled by SessionPool objects.
|
||||
|
||||
See :ref:`connpool` for information on connection pooling.
|
||||
|
||||
|
||||
.. method:: SessionPool.acquire(user=None, password=None, cclass=None, \
|
||||
purity=cx_Oracle.ATTR_PURITY_DEFAULT, tag=None, matchanytag=False, \
|
||||
shardingkey=[], supershardingkey=[])
|
||||
|
||||
Acquire a connection from the session pool and return a
|
||||
:ref:`connection object <connobj>`.
|
||||
|
||||
If the pool is homogeneous, 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:`~cx_Oracle.ATTR_PURITY_NEW`, :data:`~cx_Oracle.ATTR_PURITY_SELF`, or
|
||||
:data:`~cx_Oracle.ATTR_PURITY_DEFAULT`.
|
||||
|
||||
The tag parameter, if specified, is expected to be a string with name=value
|
||||
pairs like "k1=v1;k2=v2" and will limit the sessions that can be returned
|
||||
from a session pool unless the matchanytag parameter is set to True. In
|
||||
that case sessions with the specified tag will be preferred over others,
|
||||
but if no such sessions are available a session with a different tag may be
|
||||
returned instead. In any case, untagged sessions will always be returned if
|
||||
no sessions with the specified tag are available. Sessions are tagged when
|
||||
they are :meth:`released <SessionPool.release>` back to the pool.
|
||||
|
||||
The shardingkey and supershardingkey parameters, if specified, are expected
|
||||
to be a sequence of values which will be used to identify the database
|
||||
shard to connect to. The key values can be strings, numbers, bytes or
|
||||
dates.
|
||||
|
||||
|
||||
.. attribute:: SessionPool.busy
|
||||
|
||||
This read-only attribute returns the number of sessions currently acquired.
|
||||
|
||||
|
||||
.. method:: SessionPool.close(force=False)
|
||||
|
||||
Close the session 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:: SessionPool.drop(connection)
|
||||
|
||||
Drop the connection from the pool which is useful if the connection is no
|
||||
longer usable (such as when the session is killed).
|
||||
|
||||
|
||||
.. attribute:: SessionPool.dsn
|
||||
|
||||
This read-only attribute returns the TNS entry of the database to which a
|
||||
connection has been established.
|
||||
|
||||
|
||||
.. attribute:: SessionPool.getmode
|
||||
|
||||
This read-write attribute determines how connections are returned from the
|
||||
pool. If :data:`~cx_Oracle.SPOOL_ATTRVAL_FORCEGET` is specified, a new
|
||||
connection will be returned even if there are no free sessions in the pool.
|
||||
:data:`~cx_Oracle.SPOOL_ATTRVAL_NOWAIT` will raise an exception if there
|
||||
are no free sessions are available in the pool. If
|
||||
:data:`~cx_Oracle.SPOOL_ATTRVAL_WAIT` is specified and there are no free
|
||||
sessions in the pool, the caller will wait until a free session is
|
||||
available. :data:`~cx_Oracle.SPOOL_ATTRVAL_TIMEDWAIT` uses the value of
|
||||
:data:`~SessionPool.wait_timeout` to determine how long the caller should
|
||||
wait for a session to become available before returning an error.
|
||||
|
||||
|
||||
.. attribute:: SessionPool.homogeneous
|
||||
|
||||
This read-write boolean attribute indicates whether the pool is considered
|
||||
homogeneous or not. If the pool is not homogeneous different authentication
|
||||
can be used for each connection acquired from the pool.
|
||||
|
||||
|
||||
.. attribute:: SessionPool.increment
|
||||
|
||||
This read-only attribute returns the number of sessions that will be
|
||||
established when additional sessions need to be created.
|
||||
|
||||
|
||||
.. attribute:: SessionPool.max
|
||||
|
||||
This read-only attribute returns the maximum number of sessions that the
|
||||
session pool can control.
|
||||
|
||||
|
||||
.. attribute:: SessionPool.max_lifetime_session
|
||||
|
||||
This read-write attribute returns the maximum length of time (in seconds)
|
||||
that a pooled session may exist. Sessions 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
|
||||
session may exist. This attribute is only available in Oracle Database
|
||||
12.1.
|
||||
|
||||
.. versionadded:: 5.3
|
||||
|
||||
|
||||
.. attribute:: SessionPool.max_sessions_per_shard
|
||||
|
||||
This read-write attribute returns the number of sessions that can be created
|
||||
per shard in the pool. Setting this attribute greater than zero specifies
|
||||
the maximum number of sessions in the pool that can be used for any given
|
||||
shard in a sharded database. This lets connections in the pool be balanced
|
||||
across the shards. A value of zero will not set any maximum number of
|
||||
sessions for each shard. This attribute is only available in Oracle Client
|
||||
18.3 and higher.
|
||||
|
||||
.. versionadded:: 8.2
|
||||
|
||||
|
||||
.. attribute:: SessionPool.min
|
||||
|
||||
This read-only attribute returns the number of sessions with which the
|
||||
session pool was created and the minimum number of sessions that will be
|
||||
controlled by the session pool.
|
||||
|
||||
|
||||
.. attribute:: SessionPool.name
|
||||
|
||||
This read-only attribute returns the name assigned to the session pool by
|
||||
Oracle.
|
||||
|
||||
|
||||
.. attribute:: SessionPool.opened
|
||||
|
||||
This read-only attribute returns the number of sessions currently opened by
|
||||
the session pool.
|
||||
|
||||
|
||||
.. attribute:: SessionPool.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:`SessionPool.acquire()`. Setting ``ping_interval`` to a
|
||||
negative value disables pinging. Setting it to 0 forces a ping for every
|
||||
``acquire()`` and is not recommended.
|
||||
|
||||
Prior to cx_Oracle 8.2, the ping interval was fixed at 60 seconds.
|
||||
|
||||
.. versionadded:: 8.2
|
||||
|
||||
|
||||
.. method:: SessionPool.reconfigure([min, max, increment, getmode, timeout, \
|
||||
wait_timeout, max_lifetime_session, max_sessions_per_shard, \
|
||||
soda_metadata_cache, stmtcachesize, ping_interval])
|
||||
|
||||
Reconfigures various parameters of a connection pool. The pool size can be
|
||||
altered with ``reconfigure()`` by passing values for
|
||||
:data:`~SessionPool.min`, :data:`~SessionPool.max` or
|
||||
:data:`~SessionPool.increment`. The :data:`~SessionPool.getmode`,
|
||||
:data:`~SessionPool.timeout`, :data:`~SessionPool.wait_timeout`,
|
||||
:data:`~SessionPool.max_lifetime_session`,
|
||||
:data:`~SessionPool.max_sessions_per_shard`,
|
||||
:data:`~SessionPool.soda_metadata_cache`, :data:`~SessionPool.stmtcachesize`
|
||||
and :data:`~SessionPool.ping_interval` attributes can be set directly or
|
||||
with ``reconfigure()``.
|
||||
|
||||
All parameters are optional. Unspecified parameters will leave those pool
|
||||
attributes unchanged. The parameters are processed in two stages. After any
|
||||
size change has been processed, reconfiguration on the other parameters is
|
||||
done sequentially. If an error such as an invalid value occurs when changing
|
||||
one attribute, then an exception will be generated but any already changed
|
||||
attributes will retain their new values.
|
||||
|
||||
During reconfiguration of a pool's size, the behavior of
|
||||
:meth:`SessionPool.acquire()` depends on the ``getmode`` in effect when
|
||||
``acquire()`` is called:
|
||||
|
||||
* With mode :data:`~cx_Oracle.SPOOL_ATTRVAL_FORCEGET`, an ``acquire()`` call
|
||||
will wait until the pool has been reconfigured.
|
||||
|
||||
* With mode :data:`~cx_Oracle.SPOOL_ATTRVAL_TIMEDWAIT`, an ``acquire()`` call
|
||||
will try to acquire a connection in the time specified by
|
||||
:data:`~SessionPool.wait_timeout` and return an error if the time taken
|
||||
exceeds that value.
|
||||
|
||||
* With mode :data:`~cx_Oracle.SPOOL_ATTRVAL_WAIT`, an ``acquire()`` call
|
||||
will wait until after the pool has been reconfigured and a connection is
|
||||
available.
|
||||
|
||||
* With mode :data:`~cx_Oracle.SPOOL_ATTRVAL_NOWAIT`, if the number of busy
|
||||
connections is less than the pool size, ``acquire()`` will return a new
|
||||
connection after pool reconfiguration is complete.
|
||||
|
||||
Closing connections with :meth:`SessionPool.release()` or
|
||||
:meth:`Connection.close()` will wait until any pool size reconfiguration is
|
||||
complete.
|
||||
|
||||
Closing the connection pool with :meth:`SessionPool.close()` will wait until
|
||||
reconfiguration is complete.
|
||||
|
||||
See :ref:`Connection Pool Reconfiguration <poolreconfiguration>`.
|
||||
|
||||
.. versionadded:: 8.2
|
||||
|
||||
|
||||
.. method:: SessionPool.release(connection, tag=None)
|
||||
|
||||
Release 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, etc. 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.
|
||||
|
||||
If the tag is not None, it is expected to be a string with name=value pairs
|
||||
like "k1=v1;k2=v2" and will override the value in the property
|
||||
:attr:`Connection.tag`. If either :attr:`Connection.tag` or the tag
|
||||
parameter are not None, the connection will be retagged when it is released
|
||||
back to the pool.
|
||||
|
||||
|
||||
.. attribute:: SessionPool.soda_metadata_cache
|
||||
|
||||
This read-write boolean attribute returns whether the SODA metadata cache
|
||||
is enabled or not. Enabling the cache significantly improves the
|
||||
performance of methods :meth:`SodaDatabase.createCollection()` (when not
|
||||
specifying a value for the metadata parameter) and
|
||||
:meth:`SodaDatabase.openCollection()`. Note that the cache can become out
|
||||
of date if changes to the metadata of cached collections are made
|
||||
externally.
|
||||
|
||||
.. versionadded:: 8.2
|
||||
|
||||
|
||||
.. attribute:: SessionPool.stmtcachesize
|
||||
|
||||
This read-write attribute specifies the size of the statement cache that
|
||||
will be used for connections obtained from the pool.
|
||||
|
||||
See :ref:`Statement Caching <stmtcache>` for more information.
|
||||
|
||||
.. versionadded:: 6.0
|
||||
|
||||
|
||||
.. attribute:: SessionPool.timeout
|
||||
|
||||
This read-write attribute specifies the time (in seconds) after which idle
|
||||
sessions will be terminated in order to maintain an optimum number of open
|
||||
sessions. Note that termination only occurs when the pool is accessed. A
|
||||
value of 0 means that no idle sessions are terminated.
|
||||
|
||||
|
||||
.. attribute:: SessionPool.tnsentry
|
||||
|
||||
This read-only attribute returns the TNS entry of the database to which a
|
||||
connection has been established.
|
||||
|
||||
.. deprecated:: 8.2
|
||||
|
||||
Use the attribute :attr:`~SessionPool.dsn` instead.
|
||||
|
||||
|
||||
.. attribute:: SessionPool.username
|
||||
|
||||
This read-only attribute returns the name of the user which established the
|
||||
connection to the database.
|
||||
|
||||
|
||||
.. attribute:: SessionPool.wait_timeout
|
||||
|
||||
This read-write attribute specifies the time (in milliseconds) that the
|
||||
caller should wait for a session to become available in the pool before
|
||||
returning with an error. This value is only used if the getmode parameter
|
||||
to :meth:`cx_Oracle.SessionPool()` was the value
|
||||
:data:`cx_Oracle.SPOOL_ATTRVAL_TIMEDWAIT`.
|
||||
|
||||
.. versionadded:: 6.4
|
|
@ -1,706 +0,0 @@
|
|||
.. _soda:
|
||||
|
||||
****
|
||||
SODA
|
||||
****
|
||||
|
||||
.. note::
|
||||
|
||||
**cx_Oracle has a major new release under a new name and homepage**
|
||||
`python-oracledb <https://oracle.github.io/python-oracledb/>`__.
|
||||
|
||||
**New projects should install python-oracledb instead of cx_Oracle.**
|
||||
|
||||
`Oracle Database Simple Oracle Document Access (SODA)
|
||||
<https://docs.oracle.com/en/database/oracle/simple-oracle-document-access>`__
|
||||
allows documents to be inserted, queried, and retrieved from Oracle Database
|
||||
using a set of NoSQL-style cx_Oracle methods. By default, documents are JSON
|
||||
strings. See the :ref:`user manual <sodausermanual>` for examples.
|
||||
|
||||
.. _sodarequirements:
|
||||
|
||||
-----------------
|
||||
SODA Requirements
|
||||
-----------------
|
||||
|
||||
To use SODA, the role SODA_APP must be granted to the user. To create
|
||||
collections, users need the CREATE TABLE privilege. These can be granted by a
|
||||
DBA:
|
||||
|
||||
.. code-block:: sql
|
||||
|
||||
SQL> grant soda_app, create table to myuser;
|
||||
|
||||
Advanced users who are using Oracle sequences for keys will also need the CREATE
|
||||
SEQUENCE privilege.
|
||||
|
||||
SODA requires Oracle Client 18.3 or higher and Oracle Database 18.1 and higher.
|
||||
|
||||
.. note::
|
||||
|
||||
If you are using Oracle Database 21c (or later) and create new collections
|
||||
you need to do one of the following:
|
||||
|
||||
- Use Oracle Client libraries 21c (or later).
|
||||
|
||||
- Or, explicitly use collection metadata when creating collections and set
|
||||
the data storage type to BLOB, for example::
|
||||
|
||||
{
|
||||
"keyColumn": {
|
||||
"name":"ID"
|
||||
},
|
||||
"contentColumn": {
|
||||
"name": "JSON_DOCUMENT",
|
||||
"sqlType": "BLOB"
|
||||
},
|
||||
"versionColumn": {
|
||||
"name": "VERSION",
|
||||
"method": "UUID"
|
||||
},
|
||||
"lastModifiedColumn": {
|
||||
"name": "LAST_MODIFIED"
|
||||
},
|
||||
"creationTimeColumn": {
|
||||
"name": "CREATED_ON"
|
||||
}
|
||||
}
|
||||
|
||||
- Or, set the database initialization parameter `compatible
|
||||
<https://www.oracle.com/pls/topic/lookup?ctx=dblatest&
|
||||
id=GUID-A2E90F08-BC9F-4688-A9D0-4A948DD3F7A9>`__ to 19 or lower.
|
||||
|
||||
Otherwise you may get errors such as "ORA-40842: unsupported value JSON in
|
||||
the metadata for the field sqlType" or "ORA-40659: Data type does not match
|
||||
the specification in the collection metadata".
|
||||
|
||||
.. _sodadb:
|
||||
|
||||
--------------------
|
||||
SODA Database Object
|
||||
--------------------
|
||||
|
||||
.. note::
|
||||
|
||||
This object is an extension the DB API. It is returned by the method
|
||||
:meth:`Connection.getSodaDatabase()`.
|
||||
|
||||
|
||||
.. method:: SodaDatabase.createCollection(name, metadata=None, mapMode=False)
|
||||
|
||||
Creates a SODA collection with the given name and returns a new
|
||||
:ref:`SODA collection object <sodacoll>`. If you try to create a
|
||||
collection, and a collection with the same name and metadata already
|
||||
exists, then that existing collection is opened without error.
|
||||
|
||||
If metadata is specified, it is expected to be a string containing valid
|
||||
JSON or a dictionary that will be transformed into a JSON string. This JSON
|
||||
permits you to specify the configuration of the collection including
|
||||
storage options; specifying the presence or absence of columns for creation
|
||||
timestamp, last modified timestamp and version; whether the collection can
|
||||
store only JSON documents; and methods of key and version generation. The
|
||||
default metadata creates a collection that only supports JSON documents and
|
||||
uses system generated keys. See this `collection metadata reference
|
||||
<https://www.oracle.com/pls/topic/
|
||||
lookup?ctx=dblatest&id=GUID-49EFF3D3-9FAB-4DA6-BDE2-2650383566A3>`__
|
||||
for more information.
|
||||
|
||||
If the mapMode parameter is set to True, the new collection is mapped to an
|
||||
existing table instead of creating a table. If a collection is created in
|
||||
this way, dropping the collection will not drop the existing table either.
|
||||
|
||||
.. versionadded:: 7.0
|
||||
|
||||
|
||||
.. method:: SodaDatabase.createDocument(content, key=None, mediaType="application/json")
|
||||
|
||||
Creates a :ref:`SODA document <sodadoc>` usable for SODA write operations.
|
||||
You only need to use this method if your collection requires
|
||||
client-assigned keys or has non-JSON content; otherwise, you can pass your
|
||||
content directly to SODA write operations. SodaDocument attributes
|
||||
'createdOn', 'lastModified' and 'version' will be None.
|
||||
|
||||
The content parameter can be a dictionary or list which will be transformed
|
||||
into a JSON string and then UTF-8 encoded. It can also be a string which
|
||||
will be UTF-8 encoded or it can be a bytes object which will be stored
|
||||
unchanged. If a bytes object is provided and the content is expected to be
|
||||
JSON, note that SODA only supports UTF-8, UTF-16LE and UTF-16BE encodings.
|
||||
|
||||
The key parameter should only be supplied if the collection in which the
|
||||
document is to be placed requires client-assigned keys.
|
||||
|
||||
The mediaType parameter should only be supplied if the collection in which
|
||||
the document is to be placed supports non-JSON documents and the content
|
||||
for this document is non-JSON. Using a standard MIME type for this value is
|
||||
recommended but any string will be accepted.
|
||||
|
||||
.. versionadded:: 7.0
|
||||
|
||||
|
||||
.. method:: SodaDatabase.getCollectionNames(startName=None, limit=0)
|
||||
|
||||
Returns a list of the names of collections in the database that match the
|
||||
criteria, in alphabetical order.
|
||||
|
||||
If the startName parameter is specified, the list of names returned will
|
||||
start with this value and also contain any names that fall after this value
|
||||
in alphabetical order.
|
||||
|
||||
If the limit parameter is specified and is non-zero, the number of
|
||||
collection names returned will be limited to this value.
|
||||
|
||||
.. versionadded:: 7.0
|
||||
|
||||
|
||||
.. method:: SodaDatabase.openCollection(name)
|
||||
|
||||
Opens an existing collection with the given name and returns a new
|
||||
:ref:`SODA collection object <sodacoll>`. If a collection with that name
|
||||
does not exist, None is returned.
|
||||
|
||||
.. versionadded:: 7.0
|
||||
|
||||
|
||||
.. _sodacoll:
|
||||
|
||||
----------------------
|
||||
SODA Collection Object
|
||||
----------------------
|
||||
|
||||
.. note::
|
||||
|
||||
This object is an extension the DB API. It is used to represent SODA
|
||||
collections and is created by methods
|
||||
:meth:`SodaDatabase.createCollection()` and
|
||||
:meth:`SodaDatabase.openCollection()`.
|
||||
|
||||
|
||||
.. method:: SodaCollection.createIndex(spec)
|
||||
|
||||
Creates an index on a SODA collection. The spec is expected to be a
|
||||
dictionary or a JSON-encoded string. See this `overview
|
||||
<https://www.oracle.com/pls/topic/
|
||||
lookup?ctx=dblatest&id=GUID-4848E6A0-58A7-44FD-8D6D-A033D0CCF9CB>`__
|
||||
for information on indexes in SODA.
|
||||
|
||||
Note that a commit should be performed before attempting to create an
|
||||
index.
|
||||
|
||||
.. versionadded:: 7.0
|
||||
|
||||
|
||||
.. method:: SodaCollection.drop()
|
||||
|
||||
Drops the collection from the database, if it exists. Note that if the
|
||||
collection was created with mapMode set to True the underlying table will
|
||||
not be dropped.
|
||||
|
||||
A boolean value is returned indicating if the collection was actually
|
||||
dropped.
|
||||
|
||||
.. versionadded:: 7.0
|
||||
|
||||
|
||||
.. method:: SodaCollection.dropIndex(name, force=False)
|
||||
|
||||
Drops the index with the specified name, if it exists.
|
||||
|
||||
The force parameter, if set to True, can be used to force the dropping of
|
||||
an index that the underlying Oracle Database domain index doesn't normally
|
||||
permit. This is only applicable to spatial and JSON search indexes.
|
||||
See `here <https://www.oracle.com/pls/topic/
|
||||
lookup?ctx=dblatest&id=GUID-F60F75DF-2866-4F93-BB7F-8FCE64BF67B6>`__
|
||||
for more information.
|
||||
|
||||
A boolean value is returned indicating if the index was actually dropped.
|
||||
|
||||
.. versionadded:: 7.0
|
||||
|
||||
|
||||
.. method:: SodaCollection.find()
|
||||
|
||||
This method is used to begin an operation that will act upon documents in
|
||||
the collection. It creates and returns a
|
||||
:ref:`SodaOperation object <sodaop>` which is used to specify the criteria
|
||||
and the operation that will be performed on the documents that match that
|
||||
criteria.
|
||||
|
||||
.. versionadded:: 7.0
|
||||
|
||||
|
||||
.. method:: SodaCollection.getDataGuide()
|
||||
|
||||
Returns a :ref:`SODA document object <sodadoc>` containing property names,
|
||||
data types and lengths inferred from the JSON documents in the collection.
|
||||
It can be useful for exploring the schema of a collection. Note that this
|
||||
method is only supported for JSON-only collections where a JSON search
|
||||
index has been created with the 'dataguide' option enabled. If there are
|
||||
no documents in the collection, None is returned.
|
||||
|
||||
.. versionadded:: 7.0
|
||||
|
||||
|
||||
.. method:: SodaCollection.insertMany(docs)
|
||||
|
||||
Inserts a list of documents into the collection at one time. Each of the
|
||||
input documents can be a dictionary or list or an existing :ref:`SODA
|
||||
document object <sodadoc>`.
|
||||
|
||||
.. note::
|
||||
|
||||
This method requires Oracle Client 18.5 and higher and is available
|
||||
only as a preview.
|
||||
|
||||
.. versionadded:: 7.2
|
||||
|
||||
|
||||
.. method:: SodaCollection.insertManyAndGet(docs, hint=None)
|
||||
|
||||
Similarly to :meth:`~SodaCollection.insertMany()` this method inserts a
|
||||
list of documents into the collection at one time. The only difference is
|
||||
that it returns a list of :ref:`SODA Document objects <sodadoc>`. Note that
|
||||
for performance reasons the returned documents do not contain the content.
|
||||
|
||||
The hint parameter, if specified, supplies a hint to the database when
|
||||
processing the SODA operation. This is expected to be a string in the same
|
||||
format as a SQL hint but without any comment characters, for example
|
||||
``hint="MONITOR"``. Pass only the hint ``"MONITOR"`` (turn on monitoring)
|
||||
or ``"NO_MONITOR"`` (turn off monitoring). See the Oracle Database SQL
|
||||
Tuning Guide documentation `MONITOR and NO_MONITOR Hints
|
||||
<https://www.oracle.com/pls/topic/lookup?
|
||||
ctx=dblatest&id=GUID-19E0F73C-A959-41E4-A168-91E436DEE1F1>`__
|
||||
and `Monitoring Database Operations
|
||||
<https://www.oracle.com/pls/topic/lookup?
|
||||
ctx=dblatest&id=GUID-C941CE9D-97E1-42F8-91ED-4949B2B710BF>`__
|
||||
for more information.
|
||||
|
||||
.. note::
|
||||
|
||||
This method requires Oracle Client 18.5 and higher.
|
||||
|
||||
.. versionadded:: 7.2
|
||||
|
||||
.. versionchanged:: 8.2
|
||||
|
||||
The parameter `hint` was added. Use of the hint parameter requires
|
||||
Oracle Client 21.3 or higher (or Oracle Client 19 from 19.11).
|
||||
|
||||
|
||||
.. method:: SodaCollection.insertOne(doc)
|
||||
|
||||
Inserts a given document into the collection. The input document can be a
|
||||
dictionary or list or an existing :ref:`SODA document object <sodadoc>`.
|
||||
|
||||
.. versionadded:: 7.0
|
||||
|
||||
|
||||
.. method:: SodaCollection.insertOneAndGet(doc, hint=None)
|
||||
|
||||
Similarly to :meth:`~SodaCollection.insertOne()` this method inserts a
|
||||
given document into the collection. The only difference is that it
|
||||
returns a :ref:`SODA Document object <sodadoc>`. Note that for performance
|
||||
reasons the returned document does not contain the content.
|
||||
|
||||
The hint parameter, if specified, supplies a hint to the database when
|
||||
processing the SODA operation. This is expected to be a string in the same
|
||||
format as a SQL hint but without any comment characters, for example
|
||||
``hint="MONITOR"``. Pass only the hint ``"MONITOR"`` (turn on monitoring)
|
||||
or ``"NO_MONITOR"`` (turn off monitoring). See the Oracle Database SQL
|
||||
Tuning Guide documentation `MONITOR and NO_MONITOR Hints
|
||||
<https://www.oracle.com/pls/topic/lookup?
|
||||
ctx=dblatest&id=GUID-19E0F73C-A959-41E4-A168-91E436DEE1F1>`__
|
||||
and `Monitoring Database Operations
|
||||
<https://www.oracle.com/pls/topic/lookup?
|
||||
ctx=dblatest&id=GUID-C941CE9D-97E1-42F8-91ED-4949B2B710BF>`__
|
||||
for more information.
|
||||
|
||||
.. versionadded:: 7.0
|
||||
|
||||
.. versionchanged:: 8.2
|
||||
|
||||
The parameter `hint` was added. Use of the hint parameter requires
|
||||
Oracle Client 21.3 or higher (or Oracle Client 19 from 19.11).
|
||||
|
||||
|
||||
.. attribute:: SodaCollection.metadata
|
||||
|
||||
This read-only attribute returns a dictionary containing the metadata that
|
||||
was used to create the collection. See this `collection metadata reference
|
||||
<https://www.oracle.com/pls/topic/
|
||||
lookup?ctx=dblatest&id=GUID-49EFF3D3-9FAB-4DA6-BDE2-2650383566A3>`__
|
||||
for more information.
|
||||
|
||||
.. versionadded:: 7.0
|
||||
|
||||
|
||||
.. attribute:: SodaCollection.name
|
||||
|
||||
This read-only attribute returns the name of the collection.
|
||||
|
||||
.. versionadded:: 7.0
|
||||
|
||||
|
||||
.. method:: SodaCollection.save(doc)
|
||||
|
||||
Saves a document into the collection. This method is equivalent to
|
||||
:meth:`~SodaCollection.insertOne()` except that if client-assigned keys are
|
||||
used, and the document with the specified key already exists in the
|
||||
collection, it will be replaced with the input document.
|
||||
|
||||
This method requires Oracle Client 19.9 or higher in addition to the usual
|
||||
SODA requirements.
|
||||
|
||||
.. versionadded:: 8.0
|
||||
|
||||
|
||||
.. method:: SodaCollection.saveAndGet(doc, hint=None)
|
||||
|
||||
Saves a document into the collection. This method is equivalent to
|
||||
:meth:`~SodaCollection.insertOneAndGet()` except that if client-assigned
|
||||
keys are used, and the document with the specified key already exists in
|
||||
the collection, it will be replaced with the input document.
|
||||
|
||||
The hint parameter, if specified, supplies a hint to the database when
|
||||
processing the SODA operation. This is expected to be a string in the same
|
||||
format as a SQL hint but without any comment characters, for example
|
||||
``hint="MONITOR"``. Pass only the hint ``"MONITOR"`` (turn on monitoring)
|
||||
or ``"NO_MONITOR"`` (turn off monitoring). See the Oracle Database SQL
|
||||
Tuning Guide documentation `MONITOR and NO_MONITOR Hints
|
||||
<https://www.oracle.com/pls/topic/lookup?
|
||||
ctx=dblatest&id=GUID-19E0F73C-A959-41E4-A168-91E436DEE1F1>`__
|
||||
and `Monitoring Database Operations
|
||||
<https://www.oracle.com/pls/topic/lookup?
|
||||
ctx=dblatest&id=GUID-C941CE9D-97E1-42F8-91ED-4949B2B710BF>`__
|
||||
for more information.
|
||||
|
||||
This method requires Oracle Client 19.9 or higher in addition to the usual
|
||||
SODA requirements.
|
||||
|
||||
.. versionadded:: 8.0
|
||||
|
||||
.. versionchanged:: 8.2
|
||||
|
||||
The parameter `hint` was added. Use of the hint parameter requires
|
||||
Oracle Client 21.3 or higher (or Oracle Client 19 from 19.11).
|
||||
|
||||
|
||||
.. method:: SodaCollection.truncate()
|
||||
|
||||
Removes all of the documents in the collection, similarly to what is done
|
||||
for rows in a table by the TRUNCATE TABLE statement.
|
||||
|
||||
.. versionadded:: 8.0
|
||||
|
||||
|
||||
.. _sodadoc:
|
||||
|
||||
--------------------
|
||||
SODA Document Object
|
||||
--------------------
|
||||
|
||||
.. note::
|
||||
|
||||
This object is an extension the DB API. It is returned by the methods
|
||||
:meth:`SodaDatabase.createDocument()`,
|
||||
:meth:`SodaOperation.getDocuments()` and
|
||||
:meth:`SodaOperation.getOne()` as well as by iterating over
|
||||
:ref:`SODA document cursors <sodadoccur>`.
|
||||
|
||||
|
||||
.. attribute:: SodaDoc.createdOn
|
||||
|
||||
This read-only attribute returns the creation time of the document in
|
||||
`ISO 8601 <https://www.iso.org/iso-8601-date-and-time-format.html>`__
|
||||
format. Documents created by :meth:`SodaDatabase.createDocument()` or
|
||||
fetched from collections where this attribute is not stored will return
|
||||
None.
|
||||
|
||||
.. versionadded:: 7.0
|
||||
|
||||
|
||||
.. method:: SodaDoc.getContent()
|
||||
|
||||
Returns the content of the document as a dictionary or list. This method
|
||||
assumes that the content is application/json and will raise an exception if
|
||||
this is not the case. If there is no content, however, None will be
|
||||
returned.
|
||||
|
||||
.. versionadded:: 7.0
|
||||
|
||||
|
||||
.. method:: SodaDoc.getContentAsBytes()
|
||||
|
||||
Returns the content of the document as a bytes object. If there is no
|
||||
content, however, None will be returned.
|
||||
|
||||
.. versionadded:: 7.0
|
||||
|
||||
|
||||
.. method:: SodaDoc.getContentAsString()
|
||||
|
||||
Returns the content of the document as a string. If the document encoding
|
||||
is not known, UTF-8 will be used. If there is no content, however, None
|
||||
will be returned.
|
||||
|
||||
.. versionadded:: 7.0
|
||||
|
||||
|
||||
.. attribute:: SodaDoc.key
|
||||
|
||||
This read-only attribute returns the unique key assigned to this document.
|
||||
Documents created by :meth:`SodaDatabase.createDocument()` may not have a
|
||||
value assigned to them and return None.
|
||||
|
||||
.. versionadded:: 7.0
|
||||
|
||||
|
||||
.. attribute:: SodaDoc.lastModified
|
||||
|
||||
This read-only attribute returns the last modified time of the document in
|
||||
`ISO 8601 <https://www.iso.org/iso-8601-date-and-time-format.html>`__
|
||||
format. Documents created by :meth:`SodaDatabase.createDocument()` or
|
||||
fetched from collections where this attribute is not stored will return
|
||||
None.
|
||||
|
||||
.. versionadded:: 7.0
|
||||
|
||||
|
||||
.. attribute:: SodaDoc.mediaType
|
||||
|
||||
This read-only attribute returns the media type assigned to the document.
|
||||
By convention this is expected to be a MIME type but no checks are
|
||||
performed on this value. If a value is not specified when calling
|
||||
:meth:`SodaDatabase.createDocument()` or the document is fetched from a
|
||||
collection where this component is not stored, the string
|
||||
"application/json" is returned.
|
||||
|
||||
.. versionadded:: 7.0
|
||||
|
||||
|
||||
.. attribute:: SodaDoc.version
|
||||
|
||||
This read-only attribute returns the version assigned to this document.
|
||||
Documents created by :meth:`SodaDatabase.createDocument()` or fetched
|
||||
from collections where this attribute is not stored will return None.
|
||||
|
||||
.. versionadded:: 7.0
|
||||
|
||||
|
||||
.. _sodadoccur:
|
||||
|
||||
---------------------------
|
||||
SODA Document Cursor Object
|
||||
---------------------------
|
||||
|
||||
.. note::
|
||||
|
||||
This object is an extension the DB API. It is returned by the method
|
||||
:meth:`SodaOperation.getCursor()` and implements the iterator protocol.
|
||||
Each iteration will return a :ref:`SODA document object <sodadoc>`.
|
||||
|
||||
|
||||
.. method:: SodaDocCursor.close()
|
||||
|
||||
Close 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.
|
||||
|
||||
.. versionadded:: 7.0
|
||||
|
||||
|
||||
.. _sodaop:
|
||||
|
||||
---------------------
|
||||
SODA Operation Object
|
||||
---------------------
|
||||
|
||||
.. note::
|
||||
|
||||
This object is an extension to the DB API. It represents an operation that
|
||||
will be performed on all or some of the documents in a SODA collection. It
|
||||
is created by the method :meth:`SodaCollection.find()`.
|
||||
|
||||
|
||||
.. method:: SodaOperation.count()
|
||||
|
||||
Returns a count of the number of documents in the collection that match
|
||||
the criteria. If :meth:`~SodaOperation.skip()` or
|
||||
:meth:`~SodaOperation.limit()` were called on this object, an exception is
|
||||
raised.
|
||||
|
||||
.. versionadded:: 7.0
|
||||
|
||||
|
||||
.. method:: SodaOperation.fetchArraySize(value)
|
||||
|
||||
This is a tuning method to specify the number of documents that are
|
||||
internally fetched in batches by calls to :meth:`~SodaOperation.getCursor()`
|
||||
and :meth:`~SodaOperation.getDocuments()`. It does not affect how many
|
||||
documents are returned to the application. A value of 0 will use the default
|
||||
value (100). This method is only available in Oracle Client 19.5 and higher.
|
||||
|
||||
As a convenience, the SodaOperation object is returned so that further
|
||||
criteria can be specified by chaining methods together.
|
||||
|
||||
.. versionadded:: 8.0
|
||||
|
||||
|
||||
.. method:: SodaOperation.filter(value)
|
||||
|
||||
Sets a filter specification for complex document queries and ordering of
|
||||
JSON documents. Filter specifications must be provided as a dictionary or
|
||||
JSON-encoded string and can include comparisons, regular expressions,
|
||||
logical and spatial operators, among others. See the
|
||||
`overview of SODA filter specifications
|
||||
<https://www.oracle.com/pls/topic/
|
||||
lookup?ctx=dblatest&id=GUID-CB09C4E3-BBB1-40DC-88A8-8417821B0FBE>`__
|
||||
for more information.
|
||||
|
||||
As a convenience, the SodaOperation object is returned so that further
|
||||
criteria can be specified by chaining methods together.
|
||||
|
||||
.. versionadded:: 7.0
|
||||
|
||||
|
||||
.. method:: SodaOperation.getCursor()
|
||||
|
||||
Returns a :ref:`SODA Document Cursor object <sodadoccur>` that can be used
|
||||
to iterate over the documents that match the criteria.
|
||||
|
||||
.. versionadded:: 7.0
|
||||
|
||||
|
||||
.. method:: SodaOperation.getDocuments()
|
||||
|
||||
Returns a list of :ref:`SODA Document objects <sodadoc>` that match the
|
||||
criteria.
|
||||
|
||||
.. versionadded:: 7.0
|
||||
|
||||
|
||||
.. method:: SodaOperation.getOne()
|
||||
|
||||
Returns a single :ref:`SODA Document object <sodadoc>` that matches the
|
||||
criteria. Note that if multiple documents match the criteria only the first
|
||||
one is returned.
|
||||
|
||||
.. versionadded:: 7.0
|
||||
|
||||
|
||||
.. method:: SodaOperation.hint(value)
|
||||
|
||||
Specifies a hint that will be provided to the SODA operation when it is
|
||||
performed. This is expected to be a string in the same format as a SQL hint
|
||||
but without any comment characters, for example ``hint("MONITOR")``. Pass
|
||||
only the hint ``"MONITOR"`` (turn on monitoring) or ``"NO_MONITOR"`` (turn
|
||||
off monitoring). See the Oracle Database SQL Tuning Guide documentation
|
||||
`MONITOR and NO_MONITOR Hints
|
||||
<https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-19E0F73C-A959-41E4-A168-91E436DEE1F1>`__
|
||||
and `Monitoring Database Operations
|
||||
<https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-C941CE9D-97E1-42F8-91ED-4949B2B710BF>`__
|
||||
for more information.
|
||||
|
||||
As a convenience, the SodaOperation object is returned so that further
|
||||
criteria can be specified by chaining methods together.
|
||||
|
||||
Use of this method requires Oracle Client 21.3 or higher (or Oracle Client
|
||||
19 from 19.11).
|
||||
|
||||
.. versionadded:: 8.2
|
||||
|
||||
|
||||
.. method:: SodaOperation.key(value)
|
||||
|
||||
Specifies that the document with the specified key should be returned.
|
||||
This causes any previous calls made to this method and
|
||||
:meth:`~SodaOperation.keys()` to be ignored.
|
||||
|
||||
As a convenience, the SodaOperation object is returned so that further
|
||||
criteria can be specified by chaining methods together.
|
||||
|
||||
.. versionadded:: 7.0
|
||||
|
||||
|
||||
.. method:: SodaOperation.keys(seq)
|
||||
|
||||
Specifies that documents that match the keys found in the supplied sequence
|
||||
should be returned. This causes any previous calls made to this method and
|
||||
:meth:`~SodaOperation.key()` to be ignored.
|
||||
|
||||
As a convenience, the SodaOperation object is returned so that further
|
||||
criteria can be specified by chaining methods together.
|
||||
|
||||
.. versionadded:: 7.0
|
||||
|
||||
|
||||
.. method:: SodaOperation.limit(value)
|
||||
|
||||
Specifies that only the specified number of documents should be returned.
|
||||
This method is only usable for read operations such as
|
||||
:meth:`~SodaOperation.getCursor()` and
|
||||
:meth:`~SodaOperation.getDocuments()`. For write operations, any value set
|
||||
using this method is ignored.
|
||||
|
||||
As a convenience, the SodaOperation object is returned so that further
|
||||
criteria can be specified by chaining methods together.
|
||||
|
||||
.. versionadded:: 7.0
|
||||
|
||||
|
||||
.. method:: SodaOperation.remove()
|
||||
|
||||
Removes all of the documents in the collection that match the criteria. The
|
||||
number of documents that have been removed is returned.
|
||||
|
||||
.. versionadded:: 7.0
|
||||
|
||||
|
||||
.. method:: SodaOperation.replaceOne(doc)
|
||||
|
||||
Replaces a single document in the collection with the specified document.
|
||||
The input document can be a dictionary or list or an existing
|
||||
:ref:`SODA document object <sodadoc>`. A boolean indicating if a document
|
||||
was replaced or not is returned.
|
||||
|
||||
Currently the method :meth:`~SodaOperation.key()` must be called before
|
||||
this method can be called.
|
||||
|
||||
.. versionadded:: 7.0
|
||||
|
||||
|
||||
.. method:: SodaOperation.replaceOneAndGet(doc)
|
||||
|
||||
Similarly to :meth:`~SodaOperation.replaceOne()`, this method replaces a
|
||||
single document in the collection with the specified document. The only
|
||||
difference is that it returns a :ref:`SODA document object <sodadoc>`.
|
||||
Note that for performance reasons the returned document does not contain
|
||||
the content.
|
||||
|
||||
.. versionadded:: 7.0
|
||||
|
||||
|
||||
.. method:: SodaOperation.skip(value)
|
||||
|
||||
Specifies the number of documents that match the other criteria that will
|
||||
be skipped. This method is only usable for read operations such as
|
||||
:meth:`~SodaOperation.getCursor()` and
|
||||
:meth:`~SodaOperation.getDocuments()`. For write operations, any value set
|
||||
using this method is ignored.
|
||||
|
||||
As a convenience, the SodaOperation object is returned so that further
|
||||
criteria can be specified by chaining methods together.
|
||||
|
||||
.. versionadded:: 7.0
|
||||
|
||||
|
||||
.. method:: SodaOperation.version(value)
|
||||
|
||||
Specifies that documents with the specified version should be returned.
|
||||
Typically this is used with :meth:`~SodaOperation.key()` to implement
|
||||
optimistic locking, so that the write operation called later does not
|
||||
affect a document that someone else has modified.
|
||||
|
||||
As a convenience, the SodaOperation object is returned so that further
|
||||
criteria can be specified by chaining methods together.
|
||||
|
||||
.. versionadded:: 7.0
|
|
@ -1,282 +0,0 @@
|
|||
.. _subscrobj:
|
||||
|
||||
*******************
|
||||
Subscription Object
|
||||
*******************
|
||||
|
||||
.. note::
|
||||
|
||||
**cx_Oracle has a major new release under a new name and homepage**
|
||||
`python-oracledb <https://oracle.github.io/python-oracledb/>`__.
|
||||
|
||||
**New projects should install python-oracledb instead of cx_Oracle.**
|
||||
|
||||
.. note::
|
||||
|
||||
This object is an extension the DB API.
|
||||
|
||||
|
||||
.. attribute:: Subscription.callback
|
||||
|
||||
This read-only attribute returns the callback that was registered when the
|
||||
subscription was created.
|
||||
|
||||
|
||||
.. attribute:: Subscription.connection
|
||||
|
||||
This read-only attribute returns the connection that was used to register
|
||||
the subscription when it was created.
|
||||
|
||||
|
||||
.. attribute:: Subscription.id
|
||||
|
||||
This read-only attribute returns the value of ``REGID`` found in the
|
||||
database view ``USER_CHANGE_NOTIFICATION_REGS`` or the value of ``REG_ID``
|
||||
found in the database view ``USER_SUBSCR_REGISTRATIONS``. For AQ
|
||||
subscriptions, the value is 0.
|
||||
|
||||
|
||||
.. attribute:: Subscription.ip_address
|
||||
|
||||
This read-only attribute returns the IP address used for callback
|
||||
notifications from the database server. If not set during construction,
|
||||
this value is None.
|
||||
|
||||
.. versionadded:: 6.4
|
||||
|
||||
.. versionchanged:: 8.2
|
||||
|
||||
For consistency and compliance with the PEP 8 naming style, the
|
||||
attribute `ipAddress` was renamed to `ip_address`. The old name will
|
||||
continue to work for a period of time.
|
||||
|
||||
|
||||
.. attribute:: Subscription.name
|
||||
|
||||
This read-only attribute returns the name used to register the subscription
|
||||
when it was created.
|
||||
|
||||
.. versionadded:: 6.4
|
||||
|
||||
|
||||
.. attribute:: Subscription.namespace
|
||||
|
||||
This read-only attribute returns the namespace used to register the
|
||||
subscription when it was created.
|
||||
|
||||
|
||||
.. attribute:: Subscription.operations
|
||||
|
||||
This read-only attribute returns the operations that will send
|
||||
notifications for each table or query that is registered using this
|
||||
subscription.
|
||||
|
||||
|
||||
.. attribute:: Subscription.port
|
||||
|
||||
This read-only attribute returns the port used for callback notifications
|
||||
from the database server. If not set during construction, this value is
|
||||
zero.
|
||||
|
||||
|
||||
.. attribute:: Subscription.protocol
|
||||
|
||||
This read-only attribute returns the protocol used to register the
|
||||
subscription when it was created.
|
||||
|
||||
|
||||
.. attribute:: Subscription.qos
|
||||
|
||||
This read-only attribute returns the quality of service flags used to
|
||||
register the subscription when it was created.
|
||||
|
||||
|
||||
.. method:: Subscription.registerquery(statement, [args])
|
||||
|
||||
Register the query for subsequent notification when tables referenced by
|
||||
the query are changed. This behaves similarly to cursor.execute() but only
|
||||
queries are permitted and the args parameter must be a sequence or
|
||||
dictionary. If the qos parameter included the flag
|
||||
cx_Oracle.SUBSCR_QOS_QUERY when the subscription was created, then the ID
|
||||
for the registered query is returned; otherwise, None is returned.
|
||||
|
||||
|
||||
.. attribute:: Subscription.timeout
|
||||
|
||||
This read-only attribute returns the timeout (in seconds) that was
|
||||
specified when the subscription was created. A value of 0 indicates that
|
||||
there is no timeout.
|
||||
|
||||
|
||||
.. _msgobjects:
|
||||
|
||||
Message Objects
|
||||
---------------
|
||||
|
||||
.. note::
|
||||
|
||||
This object is created internally when notification is received and passed
|
||||
to the callback procedure specified when a subscription is created.
|
||||
|
||||
|
||||
.. attribute:: Message.consumer_name
|
||||
|
||||
This read-only attribute returns the name of the consumer which generated
|
||||
the notification. It will be populated if the subscription was created with
|
||||
the namespace :data:`cx_Oracle.SUBSCR_NAMESPACE_AQ` and the queue is a
|
||||
multiple consumer queue.
|
||||
|
||||
.. versionadded:: 6.4
|
||||
|
||||
.. versionchanged:: 8.2
|
||||
|
||||
For consistency and compliance with the PEP 8 naming style, the
|
||||
attribute `consumerName` was renamed to `consumer_name`. The old name
|
||||
will continue to work for a period of time.
|
||||
|
||||
|
||||
.. attribute:: Message.dbname
|
||||
|
||||
This read-only attribute returns the name of the database that generated
|
||||
the notification.
|
||||
|
||||
|
||||
.. attribute:: Message.queries
|
||||
|
||||
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 None if the qos parameter did not include the flag
|
||||
:data:`~cx_Oracle.SUBSCR_QOS_QUERY` when the subscription was created.
|
||||
|
||||
|
||||
.. attribute:: Message.queue_name
|
||||
|
||||
This read-only attribute returns the name of the queue which generated the
|
||||
notification. It will only be populated if the subscription was created
|
||||
with the namespace :data:`cx_Oracle.SUBSCR_NAMESPACE_AQ`.
|
||||
|
||||
.. versionadded:: 6.4
|
||||
|
||||
.. versionchanged:: 8.2
|
||||
|
||||
For consistency and compliance with the PEP 8 naming style, the
|
||||
attribute `queueName` was renamed to `queue_name`. The old name will
|
||||
continue to work for a period of time.
|
||||
|
||||
|
||||
.. attribute:: Message.registered
|
||||
|
||||
This read-only attribute returns whether the subscription which generated
|
||||
this notification is still registered with the database. The subscription
|
||||
is automatically deregistered with the database when the subscription
|
||||
timeout value is reached or when the first notification is sent (when the
|
||||
quality of service flag :data:`cx_Oracle.SUBSCR_QOS_DEREG_NFY` is used).
|
||||
|
||||
.. versionadded:: 6.4
|
||||
|
||||
|
||||
.. attribute:: Message.subscription
|
||||
|
||||
This read-only attribute returns the subscription object for which this
|
||||
notification was generated.
|
||||
|
||||
|
||||
.. attribute:: Message.tables
|
||||
|
||||
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 None if the qos parameter included the flag
|
||||
:data:`~cx_Oracle.SUBSCR_QOS_QUERY` when the subscription was created.
|
||||
|
||||
|
||||
.. attribute:: Message.txid
|
||||
|
||||
This read-only attribute returns the id of the transaction that generated
|
||||
the notification.
|
||||
|
||||
|
||||
.. attribute:: Message.type
|
||||
|
||||
This read-only attribute returns the type of message that has been sent.
|
||||
See the constants section on event types for additional information.
|
||||
|
||||
|
||||
Message Table Objects
|
||||
---------------------
|
||||
|
||||
.. note::
|
||||
|
||||
This object is created internally for each table changed when notification
|
||||
is received and is found in the tables attribute of message objects, and
|
||||
the tables attribute of message query objects.
|
||||
|
||||
|
||||
.. attribute:: MessageTable.name
|
||||
|
||||
This read-only attribute returns the name of the table that was changed.
|
||||
|
||||
|
||||
.. attribute:: MessageTable.operation
|
||||
|
||||
This read-only attribute returns the operation that took place on the table
|
||||
that was changed.
|
||||
|
||||
|
||||
.. attribute:: MessageTable.rows
|
||||
|
||||
This read-only attribute returns a list of message row objects that give
|
||||
information about the rows changed on the table. This value is only filled
|
||||
in if the qos parameter to the :meth:`Connection.subscribe()` method
|
||||
included the flag :data:`~cx_Oracle.SUBSCR_QOS_ROWIDS`.
|
||||
|
||||
|
||||
Message Row Objects
|
||||
-------------------
|
||||
|
||||
.. note::
|
||||
|
||||
This object is created internally for each row changed on a table when
|
||||
notification is received and is found in the rows attribute of message
|
||||
table objects.
|
||||
|
||||
|
||||
.. attribute:: MessageRow.operation
|
||||
|
||||
This read-only attribute returns the operation that took place on the row
|
||||
that was changed.
|
||||
|
||||
|
||||
.. attribute:: MessageRow.rowid
|
||||
|
||||
This read-only attribute returns the rowid of the row that was changed.
|
||||
|
||||
|
||||
Message Query Objects
|
||||
---------------------
|
||||
|
||||
.. note::
|
||||
|
||||
This object is created internally for each query result set changed when
|
||||
notification is received and is found in the queries attribute of message
|
||||
objects.
|
||||
|
||||
|
||||
.. attribute:: MessageQuery.id
|
||||
|
||||
This read-only attribute returns the query id of the query for which the
|
||||
result set changed. The value will match the value returned by
|
||||
Subscription.registerquery when the related query was registered.
|
||||
|
||||
|
||||
.. attribute:: MessageQuery.operation
|
||||
|
||||
This read-only attribute returns the operation that took place on the query
|
||||
result set that was changed. Valid values for this attribute are
|
||||
:data:`~cx_Oracle.EVENT_DEREG` and :data:`~cx_Oracle.EVENT_QUERYCHANGE`.
|
||||
|
||||
|
||||
.. attribute:: MessageQuery.tables
|
||||
|
||||
This read-only attribute returns a list of message table objects that give
|
||||
information about the table changes that caused the query result set to
|
||||
change for this notification.
|
|
@ -1,114 +0,0 @@
|
|||
.. _varobj:
|
||||
|
||||
****************
|
||||
Variable Objects
|
||||
****************
|
||||
|
||||
.. note::
|
||||
|
||||
**cx_Oracle has a major new release under a new name and homepage**
|
||||
`python-oracledb <https://oracle.github.io/python-oracledb/>`__.
|
||||
|
||||
**New projects should install python-oracledb instead of cx_Oracle.**
|
||||
|
||||
.. note::
|
||||
|
||||
The DB API definition does not define this object.
|
||||
|
||||
|
||||
.. attribute:: Variable.actual_elements
|
||||
|
||||
This read-only attribute returns the actual number of elements in the
|
||||
variable. This corresponds to the number of elements in a PL/SQL index-by
|
||||
table for variables that are created using the method
|
||||
:func:`Cursor.arrayvar()`. For all other variables this value will be
|
||||
identical to the attribute :attr:`~Variable.numElements`.
|
||||
|
||||
.. versionchanged:: 8.2
|
||||
|
||||
For consistency and compliance with the PEP 8 naming style, the
|
||||
attribute `actualElements` was renamed to `actual_elements`. The old
|
||||
name will continue to work for a period of time.
|
||||
|
||||
|
||||
.. attribute:: Variable.buffer_size
|
||||
|
||||
This read-only attribute returns the size of the buffer allocated for each
|
||||
element in bytes.
|
||||
|
||||
.. versionchanged:: 8.2
|
||||
|
||||
For consistency and compliance with the PEP 8 naming style, the
|
||||
attribute `bufferSize` was renamed to `buffer_size`. The old
|
||||
name will continue to work for a period of time.
|
||||
|
||||
|
||||
.. method:: Variable.getvalue([pos=0])
|
||||
|
||||
Return the value at the given position in the variable. For variables
|
||||
created using the method :func:`Cursor.arrayvar()` the value returned will
|
||||
be a list of each of the values in the PL/SQL index-by table. For variables
|
||||
bound to DML returning statements, the value returned will also be a list
|
||||
corresponding to the returned data for the given execution of the statement
|
||||
(as identified by the pos parameter).
|
||||
|
||||
|
||||
.. attribute:: Variable.inconverter
|
||||
|
||||
This read-write attribute specifies the method used to convert data from
|
||||
Python to the Oracle database. The method signature is converter(value)
|
||||
and the expected return value is the value to bind to the database. If this
|
||||
attribute is None, the value is bound directly without any conversion.
|
||||
|
||||
|
||||
.. attribute:: Variable.num_elements
|
||||
|
||||
This read-only attribute returns the number of elements allocated in an
|
||||
array, or the number of scalar items that can be fetched into the variable
|
||||
or bound to the variable.
|
||||
|
||||
.. versionchanged:: 8.2
|
||||
|
||||
For consistency and compliance with the PEP 8 naming style, the
|
||||
attribute `numElements` was renamed to `num_elements`. The old
|
||||
name will continue to work for a period of time.
|
||||
|
||||
|
||||
.. attribute:: Variable.outconverter
|
||||
|
||||
This read-write attribute specifies the method used to convert data from
|
||||
the Oracle database to Python. The method signature is converter(value)
|
||||
and the expected return value is the value to return to Python. If this
|
||||
attribute is None, the value is returned directly without any conversion.
|
||||
|
||||
|
||||
.. method:: Variable.setvalue(pos, value)
|
||||
|
||||
Set the value at the given position in the variable.
|
||||
|
||||
|
||||
.. attribute:: Variable.size
|
||||
|
||||
This read-only attribute returns the size of the variable. For strings this
|
||||
value is the size in characters. For all others, this is same value as the
|
||||
attribute bufferSize.
|
||||
|
||||
|
||||
.. attribute:: Variable.type
|
||||
|
||||
This read-only attribute returns the type of the variable. This will be an
|
||||
:ref:`Oracle Object Type <objecttype>` if the variable binds
|
||||
Oracle objects; otherwise, it will be one of the
|
||||
:ref:`database type constants <dbtypes>`.
|
||||
|
||||
.. versionchanged:: 8.0
|
||||
Database type constants are now used when the variable is not used for
|
||||
binding Oracle objects.
|
||||
|
||||
|
||||
.. attribute:: Variable.values
|
||||
|
||||
This read-only attribute returns a copy of the value of all actual
|
||||
positions in the variable as a list. This is the equivalent of calling
|
||||
:meth:`~Variable.getvalue()` for each valid position and the length will
|
||||
correspond to the value of the :attr:`~Variable.actualElements` attribute.
|
134
doc/src/conf.py
134
doc/src/conf.py
|
@ -1,134 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# cx_Oracle documentation build configuration file
|
||||
#
|
||||
# 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).
|
||||
#
|
||||
# All configuration values have a default value; values that are commented out
|
||||
# serve to show the default value.
|
||||
|
||||
import sys
|
||||
|
||||
# If your extensions are in another directory, add it here.
|
||||
#sys.path.append('some/directory')
|
||||
|
||||
# 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 = []
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = ['.templates']
|
||||
|
||||
# The suffix of source filenames.
|
||||
source_suffix = '.rst'
|
||||
|
||||
# The root toctree document.
|
||||
root_doc = master_doc = 'index'
|
||||
|
||||
# General substitutions.
|
||||
project = 'cx_Oracle'
|
||||
copyright = u'2016, 2020, 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.
|
||||
#
|
||||
# The short X.Y version.
|
||||
version = '8.3'
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = '8.3.0'
|
||||
|
||||
# There are two options for replacing |today|: either, you set today to some
|
||||
# non-false value, then it is used:
|
||||
#today = ''
|
||||
# Else, today_fmt is used as the format for a strftime call.
|
||||
today_fmt = '%B %d, %Y'
|
||||
|
||||
# List of documents that shouldn't be included in the build.
|
||||
#unused_docs = []
|
||||
|
||||
# If true, '()' will be appended to :func: etc. cross-reference text.
|
||||
#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
|
||||
|
||||
# If true, sectionauthor and moduleauthor directives will be shown in the
|
||||
# output. They are ignored by default.
|
||||
#show_authors = False
|
||||
|
||||
# The name of the Pygments (syntax highlighting) style to use.
|
||||
pygments_style = 'sphinx'
|
||||
|
||||
|
||||
# Options for HTML output
|
||||
# -----------------------
|
||||
|
||||
# The style sheet to use for HTML and HTML Help pages. A file of that name
|
||||
# must exist either in Sphinx' static/ path, or in one of the custom paths
|
||||
# given in html_static_path.
|
||||
html_style = 'default.css'
|
||||
|
||||
# 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']
|
||||
|
||||
# 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'
|
||||
|
||||
# If true, SmartyPants will be used to convert quotes and dashes to
|
||||
# typographically correct entities.
|
||||
#html_use_smartypants = True
|
||||
|
||||
# Content template for the index page.
|
||||
#html_index = ''
|
||||
|
||||
# Custom sidebar templates, maps document names to template names.
|
||||
#html_sidebars = {}
|
||||
|
||||
# Additional templates that should be rendered to pages, maps page names to
|
||||
# template names.
|
||||
#html_additional_pages = {}
|
||||
|
||||
# If false, no module index is generated.
|
||||
#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 = 'cx_Oracledoc'
|
||||
|
||||
numfig = True
|
||||
|
||||
# Options for LaTeX output
|
||||
# ------------------------
|
||||
|
||||
# The paper size ('letter' or 'a4').
|
||||
#latex_paper_size = 'letter'
|
||||
|
||||
# The font size ('10pt', '11pt' or '12pt').
|
||||
#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 = []
|
||||
|
||||
# Additional stuff for the LaTeX preamble.
|
||||
#latex_preamble = ''
|
||||
|
||||
# Documents to append as an appendix to all manuals.
|
||||
#latex_appendices = []
|
||||
|
||||
# If false, no module index is generated.
|
||||
#latex_use_modindex = True
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 88 KiB |
|
@ -1,75 +0,0 @@
|
|||
Welcome to cx_Oracle's documentation!
|
||||
=====================================
|
||||
|
||||
.. note::
|
||||
|
||||
**cx_Oracle has a major new release under a new name and homepage**
|
||||
`python-oracledb <https://oracle.github.io/python-oracledb/>`__.
|
||||
|
||||
**New projects should install python-oracledb instead of cx_Oracle.**
|
||||
|
||||
**cx_Oracle** is a module that enables access to Oracle Database and conforms
|
||||
to the Python database API specification. This module is currently tested
|
||||
against Oracle Client 21c, 19c, 18c, 12c, and 11.2, and Python 3.6, 3.7, 3.8,
|
||||
3.9 and 3.10. Older versions of cx_Oracle may be used with previous Python
|
||||
releases.
|
||||
|
||||
**cx_Oracle** is distributed under an open-source :ref:`license <license>`
|
||||
(the BSD license). A detailed description of cx_Oracle changes can be found in
|
||||
the :ref:`release notes <releasenotes>`.
|
||||
|
||||
Contents:
|
||||
|
||||
User Guide
|
||||
==========
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 3
|
||||
|
||||
user_guide/introduction.rst
|
||||
user_guide/installation.rst
|
||||
user_guide/initialization.rst
|
||||
user_guide/connection_handling.rst
|
||||
user_guide/sql_execution.rst
|
||||
user_guide/plsql_execution.rst
|
||||
user_guide/bind.rst
|
||||
user_guide/lob_data.rst
|
||||
user_guide/json_data_type.rst
|
||||
user_guide/soda.rst
|
||||
user_guide/xml_data_type.rst
|
||||
user_guide/batch_statement.rst
|
||||
user_guide/exception_handling.rst
|
||||
user_guide/aq.rst
|
||||
user_guide/cqn.rst
|
||||
user_guide/txn_management.rst
|
||||
user_guide/tuning.rst
|
||||
user_guide/globalization.rst
|
||||
user_guide/startup.rst
|
||||
user_guide/ha.rst
|
||||
user_guide/tracing_sql.rst
|
||||
|
||||
API Manual
|
||||
==========
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 3
|
||||
|
||||
api_manual/module.rst
|
||||
api_manual/connection.rst
|
||||
api_manual/cursor.rst
|
||||
api_manual/variable.rst
|
||||
api_manual/session_pool.rst
|
||||
api_manual/subscription.rst
|
||||
api_manual/lob.rst
|
||||
api_manual/object_type.rst
|
||||
api_manual/aq.rst
|
||||
api_manual/soda.rst
|
||||
api_manual/deprecations.rst
|
||||
|
||||
|
||||
Indices and tables
|
||||
==================
|
||||
|
||||
* :ref:`genindex`
|
||||
* :ref:`modindex`
|
||||
* :ref:`search`
|
|
@ -1,46 +0,0 @@
|
|||
:orphan:
|
||||
|
||||
.. _license:
|
||||
|
||||
*******
|
||||
License
|
||||
*******
|
||||
|
||||
.. include:: <isonum.txt>
|
||||
|
||||
.. centered:: **LICENSE AGREEMENT FOR CX_ORACLE**
|
||||
|
||||
Copyright |copy| 2016, 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
|
||||
Copyright |copy| 2007-2015, Anthony Tuininga. All rights reserved.
|
||||
|
||||
Copyright |copy| 2001-2007, Computronix (Canada) Ltd., Edmonton, Alberta,
|
||||
Canada. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
#. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions, and the disclaimer that follows.
|
||||
|
||||
#. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions, and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
#. Neither the names of the copyright holders nor the names of any contributors
|
||||
may be used to endorse or promote products derived from this software without
|
||||
specific prior written permission.
|
||||
|
||||
DISCLAIMER: THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
\*AS IS\* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 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.
|
||||
|
||||
Computronix |reg| is a registered trademark of Computronix (Canada) Ltd.
|
||||
|
File diff suppressed because it is too large
Load Diff
|
@ -1,218 +0,0 @@
|
|||
.. _aqusermanual:
|
||||
|
||||
****************************
|
||||
Oracle Advanced Queuing (AQ)
|
||||
****************************
|
||||
|
||||
.. note::
|
||||
|
||||
**cx_Oracle has a major new release under a new name and homepage**
|
||||
`python-oracledb <https://oracle.github.io/python-oracledb/>`__.
|
||||
|
||||
**New projects should install python-oracledb instead of cx_Oracle.**
|
||||
|
||||
`Oracle Advanced Queuing
|
||||
<https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=ADQUE>`__ is a highly
|
||||
configurable and scalable messaging feature of Oracle Database. It has
|
||||
interfaces in various languages, letting you integrate multiple tools in your
|
||||
architecture.
|
||||
|
||||
cx_Oracle 7.2 introduced an updated interface for Oracle Advanced
|
||||
Queuing.
|
||||
|
||||
There are Advanced Queuing examples in the `GitHub examples
|
||||
<https://github.com/oracle/python-cx_Oracle/tree/main/samples>`__ directory.
|
||||
|
||||
|
||||
Creating a Queue
|
||||
================
|
||||
|
||||
Before being used, queues need to be created in the database, for example in
|
||||
SQL*Plus:
|
||||
|
||||
.. code-block:: sql
|
||||
|
||||
begin
|
||||
dbms_aqadm.create_queue_table('MY_QUEUE_TABLE', 'RAW');
|
||||
dbms_aqadm.create_queue('DEMO_RAW_QUEUE', 'MY_QUEUE_TABLE');
|
||||
dbms_aqadm.start_queue('DEMO_RAW_QUEUE');
|
||||
end;
|
||||
/
|
||||
|
||||
This examples creates a RAW queue suitable for sending string or raw bytes
|
||||
messages.
|
||||
|
||||
|
||||
Enqueuing Messages
|
||||
==================
|
||||
|
||||
To send messages in Python you connect and get a :ref:`queue <queue>`. The
|
||||
queue can be used for enqueuing, dequeuing, or both as needed.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
queue = connection.queue("DEMO_RAW_QUEUE")
|
||||
|
||||
Now messages can be queued using :meth:`~Queue.enqone()`. To send three
|
||||
messages:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
PAYLOAD_DATA = [
|
||||
"The first message",
|
||||
"The second message",
|
||||
"The third message"
|
||||
]
|
||||
for data in PAYLOAD_DATA:
|
||||
queue.enqone(connection.msgproperties(payload=data))
|
||||
connection.commit()
|
||||
|
||||
Since the queue sending the messages is a RAW queue, the strings in this
|
||||
example will be internally encoded to bytes using :attr:`Connection.encoding`
|
||||
before being enqueued.
|
||||
|
||||
|
||||
Dequeuing Messages
|
||||
==================
|
||||
|
||||
Dequeuing is performed similarly. To dequeue a message call the method
|
||||
:meth:`~Queue.deqone()` as shown. Note that if the message is expected to be a
|
||||
string, the bytes must be decoded using :attr:`Connection.encoding`.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
queue = connection.queue("DEMO_RAW_QUEUE")
|
||||
msg = queue.deqOne()
|
||||
connection.commit()
|
||||
print(msg.payload.decode(connection.encoding))
|
||||
|
||||
|
||||
Using Object Queues
|
||||
===================
|
||||
|
||||
Named Oracle objects can be enqueued and dequeued as well. Given an object
|
||||
type called ``UDT_BOOK``:
|
||||
|
||||
.. code-block:: sql
|
||||
|
||||
CREATE OR REPLACE TYPE udt_book AS OBJECT (
|
||||
Title VARCHAR2(100),
|
||||
Authors VARCHAR2(100),
|
||||
Price NUMBER(5,2)
|
||||
);
|
||||
/
|
||||
|
||||
And a queue that accepts this type:
|
||||
|
||||
.. code-block:: sql
|
||||
|
||||
begin
|
||||
dbms_aqadm.create_queue_table('BOOK_QUEUE_TAB', 'UDT_BOOK');
|
||||
dbms_aqadm.create_queue('DEMO_BOOK_QUEUE', 'BOOK_QUEUE_TAB');
|
||||
dbms_aqadm.start_queue('DEMO_BOOK_QUEUE');
|
||||
end;
|
||||
/
|
||||
|
||||
You can queue messages:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
book_type = connection.gettype("UDT_BOOK")
|
||||
queue = connection.queue("DEMO_BOOK_QUEUE", book_type)
|
||||
|
||||
book = book_type.newobject()
|
||||
book.TITLE = "Quick Brown Fox"
|
||||
book.AUTHORS = "The Dog"
|
||||
book.PRICE = 123
|
||||
|
||||
queue.enqone(connection.msgproperties(payload=book))
|
||||
connection.commit()
|
||||
|
||||
Dequeuing is done like this:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
book_type = connection.gettype("UDT_BOOK")
|
||||
queue = connection.queue("DEMO_BOOK_QUEUE", book_type)
|
||||
|
||||
msg = queue.deqone()
|
||||
connection.commit()
|
||||
print(msg.payload.TITLE) # will print Quick Brown Fox
|
||||
|
||||
|
||||
Changing Queue and Message Options
|
||||
==================================
|
||||
|
||||
Refer to the :ref:`cx_Oracle AQ API <aq>` and
|
||||
`Oracle Advanced Queuing documentation
|
||||
<https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=ADQUE>`__ for details
|
||||
on all of the enqueue and dequeue options available.
|
||||
|
||||
Enqueue options can be set. For example, to make it so that an explicit
|
||||
call to :meth:`~Connection.commit()` on the connection is not needed to commit
|
||||
messages:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
queue = connection.queue("DEMO_RAW_QUEUE")
|
||||
queue.enqoptions.visibility = cx_Oracle.ENQ_IMMEDIATE
|
||||
|
||||
Dequeue options can also be set. For example, to specify not to block on
|
||||
dequeuing if no messages are available:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
queue = connection.queue("DEMO_RAW_QUEUE")
|
||||
queue.deqoptions.wait = cx_Oracle.DEQ_NO_WAIT
|
||||
|
||||
Message properties can be set when enqueuing. For example, to set an
|
||||
expiration of 60 seconds on a message:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
queue.enqone(connection.msgproperties(payload="Message", expiration=60))
|
||||
|
||||
This means that if no dequeue operation occurs within 60 seconds that the
|
||||
message will be dropped from the queue.
|
||||
|
||||
|
||||
Bulk Enqueue and Dequeue
|
||||
========================
|
||||
|
||||
The :meth:`~Queue.enqmany()` and :meth:`~Queue.deqmany()` methods can be used
|
||||
for efficient bulk message handling.
|
||||
|
||||
:meth:`~Queue.enqmany()` is similar to :meth:`~Queue.enqone()` but accepts an
|
||||
array of messages:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
messages = [
|
||||
"The first message",
|
||||
"The second message",
|
||||
"The third message",
|
||||
]
|
||||
queue = connection.queue("DEMO_RAW_QUEUE")
|
||||
queue.enqmany(connection.msgproperties(payload=m) for m in messages)
|
||||
connection.commit()
|
||||
|
||||
.. warning::
|
||||
|
||||
Calling :meth:`~Queue.enqmany()` in parallel on different connections
|
||||
acquired from the same pool may fail due to Oracle bug 29928074. Ensure
|
||||
that this function is not run in parallel, use standalone connections or
|
||||
connections from different pools, or make multiple calls to
|
||||
:meth:`~Queue.enqone()` instead. The function :meth:`~Queue.deqmany()` call
|
||||
is not affected.
|
||||
|
||||
To dequeue multiple messages at one time, use :meth:`~Queue.deqmany()`. This
|
||||
takes an argument specifying the maximum number of messages to dequeue at one
|
||||
time:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
for m in queue.deqmany(10):
|
||||
print(m.payload.decode(connection.encoding))
|
||||
|
||||
Depending on the queue properties and the number of messages available to
|
||||
dequeue, this code will print out from zero to ten messages.
|
|
@ -1,301 +0,0 @@
|
|||
.. _batchstmnt:
|
||||
|
||||
******************************************
|
||||
Batch Statement Execution and Bulk Loading
|
||||
******************************************
|
||||
|
||||
.. note::
|
||||
|
||||
**cx_Oracle has a major new release under a new name and homepage**
|
||||
`python-oracledb <https://oracle.github.io/python-oracledb/>`__.
|
||||
|
||||
**New projects should install python-oracledb instead of cx_Oracle.**
|
||||
|
||||
Inserting or updating multiple rows can be performed efficiently with
|
||||
:meth:`Cursor.executemany()`, making it easy to work with large data sets with
|
||||
cx_Oracle. This method can significantly outperform repeated calls to
|
||||
:meth:`Cursor.execute()` by reducing network transfer costs and database overheads.
|
||||
The :meth:`~Cursor.executemany()` method can also be used to execute PL/SQL
|
||||
statements multiple times at once.
|
||||
|
||||
There are examples in the `GitHub examples
|
||||
<https://github.com/oracle/python-cx_Oracle/tree/main/samples>`__
|
||||
directory.
|
||||
|
||||
The following tables will be used in the samples that follow:
|
||||
|
||||
.. code-block:: sql
|
||||
|
||||
create table ParentTable (
|
||||
ParentId number(9) not null,
|
||||
Description varchar2(60) not null,
|
||||
constraint ParentTable_pk primary key (ParentId)
|
||||
);
|
||||
|
||||
create table ChildTable (
|
||||
ChildId number(9) not null,
|
||||
ParentId number(9) not null,
|
||||
Description varchar2(60) not null,
|
||||
constraint ChildTable_pk primary key (ChildId),
|
||||
constraint ChildTable_fk foreign key (ParentId)
|
||||
references ParentTable
|
||||
);
|
||||
|
||||
|
||||
Batch Execution of SQL
|
||||
======================
|
||||
|
||||
The following example inserts five rows into the table ``ParentTable``:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
data = [
|
||||
(10, 'Parent 10'),
|
||||
(20, 'Parent 20'),
|
||||
(30, 'Parent 30'),
|
||||
(40, 'Parent 40'),
|
||||
(50, 'Parent 50')
|
||||
]
|
||||
cursor.executemany("insert into ParentTable values (:1, :2)", data)
|
||||
|
||||
This code requires only one :ref:`round-trip <roundtrips>` from the client to
|
||||
the database instead of the five round-trips that would be required for
|
||||
repeated calls to :meth:`~Cursor.execute()`. For very large data sets there
|
||||
may be an external buffer or network limits to how many rows can be processed,
|
||||
so repeated calls to ``executemany()`` may be required. The limits are based
|
||||
on both the number of rows being processed as well as the "size" of each row
|
||||
that is being processed. Repeated calls to :meth:`~Cursor.executemany()` are
|
||||
still better than repeated calls to :meth:`~Cursor.execute()`.
|
||||
|
||||
|
||||
Batch Execution of PL/SQL
|
||||
=========================
|
||||
|
||||
PL/SQL functions and procedures and anonymous PL/SQL blocks can also be called
|
||||
using :meth:`~Cursor.executemany()` in order to improve performance. For
|
||||
example:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
data = [
|
||||
(10, 'Parent 10'),
|
||||
(20, 'Parent 20'),
|
||||
(30, 'Parent 30'),
|
||||
(40, 'Parent 40'),
|
||||
(50, 'Parent 50')
|
||||
]
|
||||
cursor.executemany("begin mypkg.create_parent(:1, :2); end;", data)
|
||||
|
||||
Note that the ``batcherrors`` parameter (discussed below) cannot be used with
|
||||
PL/SQL block execution.
|
||||
|
||||
|
||||
Handling Data Errors
|
||||
====================
|
||||
|
||||
Large datasets may contain some invalid data. When using batch execution as
|
||||
discussed above, the entire batch will be discarded if a single error is
|
||||
detected, potentially eliminating the performance benefits of batch execution
|
||||
and increasing the complexity of the code required to handle those errors. If
|
||||
the parameter ``batchErrors`` is set to the value ``True`` when calling
|
||||
:meth:`~Cursor.executemany()`, however, processing will continue even if there
|
||||
are data errors in some rows, and the rows containing errors can be examined
|
||||
afterwards to determine what course the application should take. Note that if
|
||||
any errors are detected, a transaction will be started but not committed, even
|
||||
if :attr:`Connection.autocommit` is set to ``True``. After examining the errors
|
||||
and deciding what to do with them, the application needs to explicitly commit
|
||||
or roll back the transaction with :meth:`Connection.commit()` or
|
||||
:meth:`Connection.rollback()`, as needed.
|
||||
|
||||
This example shows how data errors can be identified:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
data = [
|
||||
(60, 'Parent 60'),
|
||||
(70, 'Parent 70'),
|
||||
(70, 'Parent 70 (duplicate)'),
|
||||
(80, 'Parent 80'),
|
||||
(80, 'Parent 80 (duplicate)'),
|
||||
(90, 'Parent 90')
|
||||
]
|
||||
cursor.executemany("insert into ParentTable values (:1, :2)", data,
|
||||
batcherrors=True)
|
||||
for error in cursor.getbatcherrors():
|
||||
print("Error", error.message, "at row offset", error.offset)
|
||||
|
||||
The output is::
|
||||
|
||||
Error ORA-00001: unique constraint (PYTHONDEMO.PARENTTABLE_PK) violated at row offset 2
|
||||
Error ORA-00001: unique constraint (PYTHONDEMO.PARENTTABLE_PK) violated at row offset 4
|
||||
|
||||
The row offset is the index into the array of the data that could not be
|
||||
inserted due to errors. The application could choose to commit or rollback the
|
||||
other rows that were successfully inserted. Alternatively, it could correct
|
||||
the data for the two invalid rows and attempt to insert them again before
|
||||
committing.
|
||||
|
||||
|
||||
Identifying Affected Rows
|
||||
=========================
|
||||
|
||||
When executing a DML statement using :meth:`~Cursor.execute()`, the number of
|
||||
rows affected can be examined by looking at the attribute
|
||||
:attr:`~Cursor.rowcount`. When performing batch executing with
|
||||
:meth:`Cursor.executemany()`, however, the row count will return the *total*
|
||||
number of rows that were affected. If you want to know the total number of rows
|
||||
affected by each row of data that is bound you must set the parameter
|
||||
``arraydmlrowcounts`` to ``True``, as shown:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
parent_ids_to_delete = [20, 30, 50]
|
||||
cursor.executemany("delete from ChildTable where ParentId = :1",
|
||||
[(i,) for i in parent_ids_to_delete],
|
||||
arraydmlrowcounts=True)
|
||||
row_counts = cursor.getarraydmlrowcounts()
|
||||
for parent_id, count in zip(parent_ids_to_delete, row_counts):
|
||||
print("Parent ID:", parent_id, "deleted", count, "rows.")
|
||||
|
||||
Using the data found in the `GitHub samples
|
||||
<https://github.com/oracle/python-cx_Oracle/tree/main/samples>`__ the output
|
||||
is as follows::
|
||||
|
||||
Parent ID: 20 deleted 3 rows.
|
||||
Parent ID: 30 deleted 2 rows.
|
||||
Parent ID: 50 deleted 4 rows.
|
||||
|
||||
|
||||
DML RETURNING
|
||||
=============
|
||||
|
||||
DML statements like INSERT, UPDATE, DELETE and MERGE can return values by using
|
||||
the DML RETURNING syntax. A bind variable can be created to accept this data.
|
||||
See :ref:`bind` for more information.
|
||||
|
||||
If, instead of merely deleting the rows as shown in the previous example, you
|
||||
also wanted to know some information about each of the rows that were deleted,
|
||||
you could use the following code:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
parent_ids_to_delete = [20, 30, 50]
|
||||
child_id_var = cursor.var(int, arraysize=len(parent_ids_to_delete))
|
||||
cursor.setinputsizes(None, child_id_var)
|
||||
cursor.executemany("""
|
||||
delete from ChildTable
|
||||
where ParentId = :1
|
||||
returning ChildId into :2""",
|
||||
[(i,) for i in parent_ids_to_delete])
|
||||
for ix, parent_id in enumerate(parent_ids_to_delete):
|
||||
print("Child IDs deleted for parent ID", parent_id, "are",
|
||||
child_id_var.getvalue(ix))
|
||||
|
||||
The output would then be::
|
||||
|
||||
Child IDs deleted for parent ID 20 are [1002, 1003, 1004]
|
||||
Child IDs deleted for parent ID 30 are [1005, 1006]
|
||||
Child IDs deleted for parent ID 50 are [1012, 1013, 1014, 1015]
|
||||
|
||||
Note that the bind variable created to accept the returned data must have an
|
||||
arraysize large enough to hold data for each row that is processed. Also,
|
||||
the call to :meth:`Cursor.setinputsizes()` binds this variable immediately so
|
||||
that it does not have to be passed in each row of data.
|
||||
|
||||
|
||||
Predefining Memory Areas
|
||||
========================
|
||||
|
||||
When multiple rows of data are being processed there is the possibility that
|
||||
the data is not uniform in type and size. In such cases, cx_Oracle makes some
|
||||
effort to accommodate such differences. Type determination for each column is
|
||||
deferred until a value that is not ``None`` is found in the column's data. If
|
||||
all values in a particular column are ``None``, then cx_Oracle assumes the type
|
||||
is a string and has a length of 1. cx_Oracle will also adjust the size of the
|
||||
buffers used to store strings and bytes when a longer value is encountered in
|
||||
the data. These sorts of operations incur overhead as memory has to be
|
||||
reallocated and data copied. To eliminate this overhead, using
|
||||
:meth:`~Cursor.setinputsizes()` tells cx_Oracle about the type and size of the
|
||||
data that is going to be used.
|
||||
|
||||
Consider the following code:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
data = [
|
||||
(110, "Parent 110"),
|
||||
(2000, "Parent 2000"),
|
||||
(30000, "Parent 30000"),
|
||||
(400000, "Parent 400000"),
|
||||
(5000000, "Parent 5000000")
|
||||
]
|
||||
cursor.setinputsizes(None, 20)
|
||||
cursor.executemany("""
|
||||
insert into ParentTable (ParentId, Description)
|
||||
values (:1, :2)""", data)
|
||||
|
||||
In this example, without the call to :meth:`~Cursor.setinputsizes()`, cx_Oracle
|
||||
would perform five allocations of increasing size as it discovered each new,
|
||||
longer string. However ``cursor.setinputsizes(None, 20)`` tells cx_Oracle that
|
||||
the maximum size of the strings that will be processed is 20 characters. Since
|
||||
cx_Oracle allocates memory for each row based on this value, it is best not to
|
||||
oversize it. The first parameter of ``None`` tells cx_Oracle that its default
|
||||
processing will be sufficient.
|
||||
|
||||
Loading CSV Files into Oracle Database
|
||||
======================================
|
||||
|
||||
The :meth:`Cursor.executemany()` method and Python's `csv module
|
||||
<https://docs.python.org/3/library/csv.html#module-csv>`__ can be used to
|
||||
efficiently load CSV (Comma Separated Values) files. For example, consider the
|
||||
file ``data.csv``::
|
||||
|
||||
101,Abel
|
||||
154,Baker
|
||||
132,Charlie
|
||||
199,Delta
|
||||
. . .
|
||||
|
||||
And the schema:
|
||||
|
||||
.. code-block:: sql
|
||||
|
||||
create table test (id number, name varchar2(25));
|
||||
|
||||
Data loading can be done in batches of records since the number of records may
|
||||
prevent all data being inserted at once:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import cx_Oracle
|
||||
import csv
|
||||
|
||||
# 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)
|
||||
|
||||
# Adjust the number of rows to be inserted in each iteration
|
||||
# to meet your memory and performance requirements
|
||||
batch_size = 10000
|
||||
|
||||
with open('testsp.csv', 'r') as csv_file:
|
||||
csv_reader = csv.reader(csv_file, delimiter=',')
|
||||
sql = "insert into test (id,name) values (:1, :2)"
|
||||
data = []
|
||||
for line in csv_reader:
|
||||
data.append((line[0], line[1]))
|
||||
if len(data) % batch_size == 0:
|
||||
cursor.executemany(sql, data)
|
||||
data = []
|
||||
if data:
|
||||
cursor.executemany(sql, data)
|
||||
con.commit()
|
||||
|
||||
|
||||
Depending on data sizes and business requirements, database changes such as
|
||||
temporarily disabling redo logging on the table, or disabling indexes may also
|
||||
be beneficial.
|
|
@ -1,812 +0,0 @@
|
|||
.. _bind:
|
||||
|
||||
********************
|
||||
Using Bind Variables
|
||||
********************
|
||||
|
||||
.. note::
|
||||
|
||||
**cx_Oracle has a major new release under a new name and homepage**
|
||||
`python-oracledb <https://oracle.github.io/python-oracledb/>`__.
|
||||
|
||||
**New projects should install python-oracledb instead of cx_Oracle.**
|
||||
|
||||
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. 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)"""
|
||||
cursor.execute(sql, [280, "Facility"])
|
||||
|
||||
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. 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.
|
||||
|
||||
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 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. 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
|
||||
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 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.
|
||||
|
||||
A positional bind is performed when a list of bind values are passed to the
|
||||
execute() call. For example:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
cursor.execute("""
|
||||
insert into departments (department_id, department_name)
|
||||
values (:dept_id, :dept_name)""", [280, "Facility"])
|
||||
|
||||
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
|
||||
==============
|
||||
|
||||
The caller can supply data to the database (IN), the database can return
|
||||
data to the caller (OUT) or the caller can supply initial data to the
|
||||
database and the database can supply the modified data back to the caller
|
||||
(IN/OUT). This is known as the bind direction.
|
||||
|
||||
The examples shown above have all supplied data to the database and are
|
||||
therefore classified as IN bind variables. In order to have the database return
|
||||
data to the caller, a variable must be created. This is done by calling the
|
||||
method :func:`Cursor.var()`, which identifies the type of data that will be
|
||||
found in that bind variable and its maximum size among other things.
|
||||
|
||||
Here is an example showing how to use OUT binds. It calculates the sum of the
|
||||
integers 8 and 7 and stores the result in an OUT bind variable of type integer:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
out_val = cursor.var(int)
|
||||
cursor.execute("""
|
||||
begin
|
||||
:out_val := :in_bind_var1 + :in_bind_var2;
|
||||
end;""",
|
||||
out_val=out_val, in_bind_var1=8, in_bind_var2=7)
|
||||
print(out_val.getvalue()) # will print 15
|
||||
|
||||
If instead of simply getting data back you wish to supply an initial value to
|
||||
the database, you can set the variable's initial value. This example is the
|
||||
same as the previous one but it sets the initial value first:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
in_out_var = cursor.var(int)
|
||||
in_out_var.setvalue(0, 25)
|
||||
cursor.execute("""
|
||||
begin
|
||||
:in_out_bind_var := :in_out_bind_var + :in_bind_var1 +
|
||||
:in_bind_var2;
|
||||
end;""",
|
||||
in_out_bind_var=in_out_var, in_bind_var1=8, in_bind_var2=7)
|
||||
print(in_out_var.getvalue()) # will print 40
|
||||
|
||||
When binding data to parameters of PL/SQL procedures that are declared as OUT
|
||||
parameters, it is worth noting that any value that is set in the bind variable
|
||||
will be ignored. In addition, any parameters declared as IN/OUT that do not
|
||||
have a value set will start out with a value of ``null``.
|
||||
|
||||
|
||||
Binding Null Values
|
||||
===================
|
||||
|
||||
In cx_Oracle, null values are represented by the Python singleton ``None``.
|
||||
|
||||
For example:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
cursor.execute("""
|
||||
insert into departments (department_id, department_name)
|
||||
values (:dept_id, :dept_name)""", dept_id=280, dept_name=None)
|
||||
|
||||
In this specific case, because the ``DEPARTMENT_NAME`` column is defined as a
|
||||
``NOT NULL`` column, an error will occur::
|
||||
|
||||
cx_Oracle.IntegrityError: ORA-01400: cannot insert NULL into ("HR"."DEPARTMENTS"."DEPARTMENT_NAME")
|
||||
|
||||
|
||||
If this value is bound directly, cx_Oracle assumes it to be a string
|
||||
(equivalent to a VARCHAR2 column). If you need to use a different Oracle type
|
||||
you will need to make a call to :func:`Cursor.setinputsizes()` or create a bind
|
||||
variable with the correct type by calling :func:`Cursor.var()`.
|
||||
|
||||
|
||||
Binding ROWID Values
|
||||
====================
|
||||
|
||||
The pseudo-column ``ROWID`` uniquely identifies a row within a table. In
|
||||
cx_Oracle, ROWID values are represented as strings. The example below shows
|
||||
fetching a row and then updating that row by binding its rowid:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# fetch the row
|
||||
cursor.execute("""
|
||||
select rowid, manager_id
|
||||
from departments
|
||||
where department_id = :dept_id""", dept_id=280)
|
||||
rowid, manager_id = cursor.fetchone()
|
||||
|
||||
# update the row by binding ROWID
|
||||
cursor.execute("""
|
||||
update departments set
|
||||
manager_id = :manager_id
|
||||
where rowid = :rid""", manager_id=205, rid=rowid)
|
||||
|
||||
|
||||
DML RETURNING Bind Variables
|
||||
============================
|
||||
|
||||
When a RETURNING clause is used with a DML statement like UPDATE,
|
||||
INSERT, or DELETE, the values are returned to the application through
|
||||
the use of OUT bind variables. Consider the following example:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# The RETURNING INTO bind variable is a string
|
||||
dept_name = cursor.var(str)
|
||||
|
||||
cursor.execute("""
|
||||
update departments set
|
||||
location_id = :loc_id
|
||||
where department_id = :dept_id
|
||||
returning department_name into :dept_name""",
|
||||
loc_id=1700, dept_id=50, dept_name=dept_name)
|
||||
print(dept_name.getvalue()) # will print ['Shipping']
|
||||
|
||||
In the above example, since the WHERE clause matches only one row, the output
|
||||
contains a single item in the list. If the WHERE clause matched multiple rows,
|
||||
however, the output would contain as many items as there were rows that were
|
||||
updated.
|
||||
|
||||
No duplicate binds are allowed in a DML statement with a RETURNING clause, and
|
||||
no duplication is allowed between bind variables in the DML section and the
|
||||
RETURNING section of the statement.
|
||||
|
||||
|
||||
LOB Bind Variables
|
||||
==================
|
||||
|
||||
Database CLOBs, NCLOBS, BLOBs and BFILEs can be bound with types
|
||||
:attr:`cx_Oracle.DB_TYPE_CLOB`, :attr:`cx_Oracle.DB_TYPE_NCLOB`,
|
||||
:attr:`cx_Oracle.DB_TYPE_BLOB` and :attr:`cx_Oracle.DB_TYPE_BFILE`
|
||||
respectively. LOBs fetched from the database or created with
|
||||
:meth:`Connection.createlob()` can also be bound.
|
||||
|
||||
LOBs may represent Oracle Database persistent LOBs (those stored in tables) or
|
||||
temporary LOBs (such as those created with :meth:`Connection.createlob()` or
|
||||
returned by some SQL and PL/SQL operations).
|
||||
|
||||
LOBs can be used as IN, OUT or IN/OUT bind variables.
|
||||
|
||||
See :ref:`lobdata` for examples.
|
||||
|
||||
.. _refcur:
|
||||
|
||||
REF CURSOR Bind Variables
|
||||
=========================
|
||||
|
||||
cx_Oracle provides the ability to bind and define PL/SQL REF cursors. As an
|
||||
example, consider the PL/SQL procedure:
|
||||
|
||||
.. code-block:: sql
|
||||
|
||||
CREATE OR REPLACE PROCEDURE find_employees (
|
||||
p_query IN VARCHAR2,
|
||||
p_results OUT SYS_REFCURSOR
|
||||
) AS
|
||||
BEGIN
|
||||
OPEN p_results FOR
|
||||
SELECT employee_id, first_name, last_name
|
||||
FROM employees
|
||||
WHERE UPPER(first_name || ' ' || last_name || ' ' || email)
|
||||
LIKE '%' || UPPER(p_query) || '%';
|
||||
END;
|
||||
/
|
||||
|
||||
A newly opened cursor can be bound to the REF CURSOR parameter, as shown in the
|
||||
following Python code. After the PL/SQL procedure has been called with
|
||||
:meth:`Cursor.callproc()`, the cursor can then be fetched just like any other
|
||||
cursor which had executed a SQL query:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
ref_cursor = connection.cursor()
|
||||
cursor.callproc("find_employees", ['Smith', ref_cursor])
|
||||
for row in ref_cursor:
|
||||
print(row)
|
||||
|
||||
With Oracle's `sample HR schema
|
||||
<https://github.com/oracle/db-sample-schemas>`__ there are two
|
||||
employees with the last name 'Smith' so the result is::
|
||||
|
||||
(159, 'Lindsey', 'Smith')
|
||||
(171, 'William', 'Smith')
|
||||
|
||||
To return a REF CURSOR from a PL/SQL function, use ``cx_Oracle.DB_TYPE_CURSOR`` for the
|
||||
return type of :meth:`Cursor.callfunc()`:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
ref_cursor = cursor.callfunc('example_package.f_get_cursor',
|
||||
cx_Oracle.DB_TYPE_CURSOR)
|
||||
for row in ref_cursor:
|
||||
print(row)
|
||||
|
||||
See :ref:`tuning` for information on how to tune REF CURSORS.
|
||||
|
||||
Binding PL/SQL Collections
|
||||
==========================
|
||||
|
||||
PL/SQL Collections like Associative Arrays can be bound as IN, OUT, and IN/OUT
|
||||
variables. When binding IN values, an array can be passed directly as shown in
|
||||
this example, which sums up the lengths of all of the strings in the provided
|
||||
array. First the PL/SQL package definition:
|
||||
|
||||
.. code-block:: sql
|
||||
|
||||
create or replace package mypkg as
|
||||
|
||||
type udt_StringList is table of varchar2(100) index by binary_integer;
|
||||
|
||||
function DemoCollectionIn (
|
||||
a_Values udt_StringList
|
||||
) return number;
|
||||
|
||||
end;
|
||||
/
|
||||
|
||||
create or replace package body mypkg as
|
||||
|
||||
function DemoCollectionIn (
|
||||
a_Values udt_StringList
|
||||
) return number is
|
||||
t_ReturnValue number := 0;
|
||||
begin
|
||||
for i in 1..a_Values.count loop
|
||||
t_ReturnValue := t_ReturnValue + length(a_Values(i));
|
||||
end loop;
|
||||
return t_ReturnValue;
|
||||
end;
|
||||
|
||||
end;
|
||||
/
|
||||
|
||||
Then the Python code:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
values = ["String One", "String Two", "String Three"]
|
||||
return_val = cursor.callfunc("mypkg.DemoCollectionIn", int, [values])
|
||||
print(return_val) # will print 32
|
||||
|
||||
In order get values back from the database, a bind variable must be created
|
||||
using :meth:`Cursor.arrayvar()`. The first parameter to this method is a Python
|
||||
type that cx_Oracle knows how to handle or one of the cx_Oracle :ref:`types`.
|
||||
The second parameter is the maximum number of elements that the array can hold
|
||||
or an array providing the value (and indirectly the maximum length). The final
|
||||
parameter is optional and only used for strings and bytes. It identifies the
|
||||
maximum length of the strings and bytes that can be stored in the array. If not
|
||||
specified, the length defaults to 4000 bytes.
|
||||
|
||||
Consider the following PL/SQL package:
|
||||
|
||||
.. code-block:: sql
|
||||
|
||||
create or replace package mypkg as
|
||||
|
||||
type udt_StringList is table of varchar2(100) index by binary_integer;
|
||||
|
||||
procedure DemoCollectionOut (
|
||||
a_NumElements number,
|
||||
a_Values out nocopy udt_StringList
|
||||
);
|
||||
|
||||
procedure DemoCollectionInOut (
|
||||
a_Values in out nocopy udt_StringList
|
||||
);
|
||||
|
||||
end;
|
||||
/
|
||||
|
||||
create or replace package body mypkg as
|
||||
|
||||
procedure DemoCollectionOut (
|
||||
a_NumElements number,
|
||||
a_Values out nocopy udt_StringList
|
||||
) is
|
||||
begin
|
||||
for i in 1..a_NumElements loop
|
||||
a_Values(i) := 'Demo out element #' || to_char(i);
|
||||
end loop;
|
||||
end;
|
||||
|
||||
procedure DemoCollectionInOut (
|
||||
a_Values in out nocopy udt_StringList
|
||||
) is
|
||||
begin
|
||||
for i in 1..a_Values.count loop
|
||||
a_Values(i) := 'Converted element #' || to_char(i) ||
|
||||
' originally had length ' || length(a_Values(i));
|
||||
end loop;
|
||||
end;
|
||||
|
||||
end;
|
||||
/
|
||||
|
||||
The Python code to process an OUT collection would look as follows. Note the
|
||||
call to :meth:`Cursor.arrayvar()` which creates space for an array of strings.
|
||||
Each string would permit up to 100 bytes and only 10 strings would be
|
||||
permitted. If the PL/SQL block exceeds the maximum number of strings allowed
|
||||
the error ``ORA-06513: PL/SQL: index for PL/SQL table out of range for host
|
||||
language array`` would be raised.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
out_array_var = cursor.arrayvar(str, 10, 100)
|
||||
cursor.callproc("mypkg.DemoCollectionOut", [5, out_array_var])
|
||||
for val in out_array_var.getvalue():
|
||||
print(val)
|
||||
|
||||
This would produce the following output::
|
||||
|
||||
Demo out element #1
|
||||
Demo out element #2
|
||||
Demo out element #3
|
||||
Demo out element #4
|
||||
Demo out element #5
|
||||
|
||||
The Python code to process an IN/OUT collections is similar. Note the different
|
||||
call to :meth:`Cursor.arrayvar()` which creates space for an array of strings,
|
||||
but uses an array to determine both the maximum length of the array and its
|
||||
initial value.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
in_values = ["String One", "String Two", "String Three", "String Four"]
|
||||
in_out_array_var = cursor.arrayvar(str, in_values)
|
||||
cursor.callproc("mypkg.DemoCollectionInOut", [in_out_array_var])
|
||||
for val in in_out_array_var.getvalue():
|
||||
print(val)
|
||||
|
||||
This would produce the following output::
|
||||
|
||||
Converted element #1 originally had length 10
|
||||
Converted element #2 originally had length 10
|
||||
Converted element #3 originally had length 12
|
||||
Converted element #4 originally had length 11
|
||||
|
||||
If an array variable needs to have an initial value but also needs to allow
|
||||
for more elements than the initial value contains, the following code can be
|
||||
used instead:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
in_out_array_var = cursor.arrayvar(str, 10, 100)
|
||||
in_out_array_var.setvalue(0, ["String One", "String Two"])
|
||||
|
||||
All of the collections that have been bound in preceding examples have used
|
||||
contiguous array elements. If an associative array with sparse array elements
|
||||
is needed, a different approach is required. Consider the following PL/SQL
|
||||
code:
|
||||
|
||||
.. code-block:: sql
|
||||
|
||||
create or replace package mypkg as
|
||||
|
||||
type udt_StringList is table of varchar2(100) index by binary_integer;
|
||||
|
||||
procedure DemoCollectionOut (
|
||||
a_Value out nocopy udt_StringList
|
||||
);
|
||||
|
||||
end;
|
||||
/
|
||||
|
||||
create or replace package body mypkg as
|
||||
|
||||
procedure DemoCollectionOut (
|
||||
a_Value out nocopy udt_StringList
|
||||
) is
|
||||
begin
|
||||
a_Value(-1048576) := 'First element';
|
||||
a_Value(-576) := 'Second element';
|
||||
a_Value(284) := 'Third element';
|
||||
a_Value(8388608) := 'Fourth element';
|
||||
end;
|
||||
|
||||
end;
|
||||
/
|
||||
|
||||
Note that the collection element indices are separated by large values. The
|
||||
technique used above would fail with the exception ``ORA-06513: PL/SQL: index
|
||||
for PL/SQL table out of range for host language array``. The code required to
|
||||
process this collection looks like this instead:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
collection_type = connection.gettype("MYPKG.UDT_STRINGLIST")
|
||||
collection = collection_type.newobject()
|
||||
cursor.callproc("mypkg.DemoCollectionOut", [collection])
|
||||
print(collection.aslist())
|
||||
|
||||
This produces the output::
|
||||
|
||||
['First element', 'Second element', 'Third element', 'Fourth element']
|
||||
|
||||
Note the use of :meth:`Object.aslist()` which returns the collection element
|
||||
values in index order as a simple Python list. The indices themselves are lost
|
||||
in this approach. Starting from cx_Oracle 7.0, the associative array can be
|
||||
turned into a Python dictionary using :meth:`Object.asdict()`. If that value
|
||||
was printed in the previous example instead, the output would be::
|
||||
|
||||
{-1048576: 'First element', -576: 'Second element', 284: 'Third element', 8388608: 'Fourth element'}
|
||||
|
||||
If the elements need to be traversed in index order, the methods
|
||||
:meth:`Object.first()` and :meth:`Object.next()` can be used. The method
|
||||
:meth:`Object.getelement()` can be used to acquire the element at a particular
|
||||
index. This is shown in the following code:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
ix = collection.first()
|
||||
while ix is not None:
|
||||
print(ix, "->", collection.getelement(ix))
|
||||
ix = collection.next(ix)
|
||||
|
||||
This produces the output::
|
||||
|
||||
-1048576 -> First element
|
||||
-576 -> Second element
|
||||
284 -> Third element
|
||||
8388608 -> Fourth element
|
||||
|
||||
Similarly, the elements can be traversed in reverse index order using the
|
||||
methods :meth:`Object.last()` and :meth:`Object.prev()` as shown in the
|
||||
following code:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
ix = collection.last()
|
||||
while ix is not None:
|
||||
print(ix, "->", collection.getelement(ix))
|
||||
ix = collection.prev(ix)
|
||||
|
||||
This produces the output::
|
||||
|
||||
8388608 -> Fourth element
|
||||
284 -> Third element
|
||||
-576 -> Second element
|
||||
-1048576 -> First element
|
||||
|
||||
|
||||
Binding PL/SQL Records
|
||||
======================
|
||||
|
||||
PL/SQL record type objects can also be bound for IN, OUT and IN/OUT
|
||||
bind variables. For example:
|
||||
|
||||
.. code-block:: sql
|
||||
|
||||
create or replace package mypkg as
|
||||
|
||||
type udt_DemoRecord is record (
|
||||
NumberValue number,
|
||||
StringValue varchar2(30),
|
||||
DateValue date,
|
||||
BooleanValue boolean
|
||||
);
|
||||
|
||||
procedure DemoRecordsInOut (
|
||||
a_Value in out nocopy udt_DemoRecord
|
||||
);
|
||||
|
||||
end;
|
||||
/
|
||||
|
||||
create or replace package body mypkg as
|
||||
|
||||
procedure DemoRecordsInOut (
|
||||
a_Value in out nocopy udt_DemoRecord
|
||||
) is
|
||||
begin
|
||||
a_Value.NumberValue := a_Value.NumberValue * 2;
|
||||
a_Value.StringValue := a_Value.StringValue || ' (Modified)';
|
||||
a_Value.DateValue := a_Value.DateValue + 5;
|
||||
a_Value.BooleanValue := not a_Value.BooleanValue;
|
||||
end;
|
||||
|
||||
end;
|
||||
/
|
||||
|
||||
Then this Python code can be used to call the stored procedure which will
|
||||
update the record:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# create and populate a record
|
||||
record_type = connection.gettype("MYPKG.UDT_DEMORECORD")
|
||||
record = record_type.newobject()
|
||||
record.NUMBERVALUE = 6
|
||||
record.STRINGVALUE = "Test String"
|
||||
record.DATEVALUE = datetime.datetime(2016, 5, 28)
|
||||
record.BOOLEANVALUE = False
|
||||
|
||||
# show the original values
|
||||
print("NUMBERVALUE ->", record.NUMBERVALUE)
|
||||
print("STRINGVALUE ->", record.STRINGVALUE)
|
||||
print("DATEVALUE ->", record.DATEVALUE)
|
||||
print("BOOLEANVALUE ->", record.BOOLEANVALUE)
|
||||
print()
|
||||
|
||||
# call the stored procedure which will modify the record
|
||||
cursor.callproc("mypkg.DemoRecordsInOut", [record])
|
||||
|
||||
# show the modified values
|
||||
print("NUMBERVALUE ->", record.NUMBERVALUE)
|
||||
print("STRINGVALUE ->", record.STRINGVALUE)
|
||||
print("DATEVALUE ->", record.DATEVALUE)
|
||||
print("BOOLEANVALUE ->", record.BOOLEANVALUE)
|
||||
|
||||
This will produce the following output::
|
||||
|
||||
NUMBERVALUE -> 6
|
||||
STRINGVALUE -> Test String
|
||||
DATEVALUE -> 2016-05-28 00:00:00
|
||||
BOOLEANVALUE -> False
|
||||
|
||||
NUMBERVALUE -> 12
|
||||
STRINGVALUE -> Test String (Modified)
|
||||
DATEVALUE -> 2016-06-02 00:00:00
|
||||
BOOLEANVALUE -> True
|
||||
|
||||
Note that when manipulating records, all of the attributes must be set by the
|
||||
Python program in order to avoid an Oracle Client bug which will result in
|
||||
unexpected values or the Python application segfaulting.
|
||||
|
||||
.. _spatial:
|
||||
|
||||
Binding Spatial Datatypes
|
||||
=========================
|
||||
|
||||
Oracle Spatial datatypes objects can be represented by Python objects and their
|
||||
attribute values can be read and updated. The objects can further be bound and
|
||||
committed to database. This is similar to the examples above.
|
||||
|
||||
An example of fetching SDO_GEOMETRY is in :ref:`Oracle Database Objects and
|
||||
Collections <fetchobjects>`.
|
||||
|
||||
|
||||
.. _inputtypehandlers:
|
||||
|
||||
Changing Bind Data Types using an Input Type Handler
|
||||
====================================================
|
||||
|
||||
Input Type Handlers allow applications to change how data is bound to
|
||||
statements, or even to enable new types to be bound directly.
|
||||
|
||||
An input type handler is enabled by setting the attribute
|
||||
:attr:`Cursor.inputtypehandler` or :attr:`Connection.inputtypehandler`.
|
||||
|
||||
Input type handlers can be combined with variable converters to bind Python
|
||||
objects seamlessly:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# A standard Python object
|
||||
class Building:
|
||||
|
||||
def __init__(self, build_id, description, num_floors, date_built):
|
||||
self.building_id = build_id
|
||||
self.description = description
|
||||
self.num_floors = num_floors
|
||||
self.date_built = date_built
|
||||
|
||||
building = Building(1, "Skyscraper 1", 5, datetime.date(2001, 5, 24))
|
||||
|
||||
# Get Python representation of the Oracle user defined type UDT_BUILDING
|
||||
obj_type = con.gettype("UDT_BUILDING")
|
||||
|
||||
# convert a Python Building object to the Oracle user defined type
|
||||
# UDT_BUILDING
|
||||
def building_in_converter(value):
|
||||
obj = obj_type.newobject()
|
||||
obj.BUILDINGID = value.building_id
|
||||
obj.DESCRIPTION = value.description
|
||||
obj.NUMFLOORS = value.num_floors
|
||||
obj.DATEBUILT = value.date_built
|
||||
return obj
|
||||
|
||||
def input_type_handler(cursor, value, num_elements):
|
||||
if isinstance(value, Building):
|
||||
return cursor.var(obj_type, arraysize=num_elements,
|
||||
inconverter=building_in_converter)
|
||||
|
||||
|
||||
# With the input type handler, the bound Python object is converted
|
||||
# to the required Oracle object before being inserted
|
||||
cur.inputtypehandler = input_type_handler
|
||||
cur.execute("insert into myTable values (:1, :2)", (1, building))
|
||||
|
||||
|
||||
Binding Multiple Values to a SQL WHERE IN Clause
|
||||
================================================
|
||||
|
||||
To use a SQL IN clause with multiple values, use one bind variable per
|
||||
value. You cannot directly bind a Python list or dictionary to a single bind
|
||||
variable. For example, to use two values in an IN clause:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
cursor.execute("""
|
||||
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)
|
||||
|
||||
This gives the output::
|
||||
|
||||
(159, 'Lindsey', 'Smith')
|
||||
(171, 'William', 'Smith')
|
||||
(176, 'Jonathon', 'Taylor')
|
||||
(180, 'Winston', 'Taylor')
|
||||
|
||||
If the query is executed multiple times with differing numbers of values, a
|
||||
bind variable should be included for each possible value. When the statement is
|
||||
executed but the maximum number of values has not been supplied, the value
|
||||
``None`` can be bound for missing values. For example, if the query above is
|
||||
used for up to 5 values, the code would be:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
cursor.execute("""
|
||||
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)
|
||||
|
||||
This will produce the same output as the original example. Reusing the same SQL
|
||||
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.
|
||||
|
||||
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 = [":" + 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)
|
||||
|
||||
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>' will depend
|
||||
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 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 /
|
||||
|
||||
then the application could do:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
type_obj = connection.gettype("NAME_ARRAY")
|
||||
obj = type_obj.newobject()
|
||||
obj.extend(["Smith", "Taylor"])
|
||||
cursor.execute("""select employee_id, first_name, last_name
|
||||
from employees
|
||||
where last_name in (select * from table(:1))""",
|
||||
[obj])
|
||||
for row in cursor:
|
||||
print(row)
|
||||
|
||||
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
|
||||
==============================
|
||||
|
||||
Column and table names cannot be bound in SQL queries. You can concatenate
|
||||
text to build up a SQL statement, but make sure you use an Allow List or other
|
||||
means to validate the data in order to avoid SQL Injection security issues:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
table_allow_list = ['employees', 'departments']
|
||||
table_name = get_table_name() # get the table name from user input
|
||||
if table_name.lower() not in table_allow_list:
|
||||
raise Exception('Invalid table name')
|
||||
sql = f'select * from {table_name}'
|
||||
|
||||
Binding column names can be done either by using the above method or by using a
|
||||
CASE statement. The example below demonstrates binding a column name in an
|
||||
ORDER BY clause:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
sql = """
|
||||
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])
|
||||
|
||||
Depending on the name provided by the user, the query results will be
|
||||
ordered either by the column ``DEPARTMENT_ID`` or the column ``MANAGER_ID``.
|
File diff suppressed because it is too large
Load Diff
|
@ -1,165 +0,0 @@
|
|||
.. _cqn:
|
||||
|
||||
***********************************
|
||||
Continuous Query Notification (CQN)
|
||||
***********************************
|
||||
|
||||
.. note::
|
||||
|
||||
**cx_Oracle has a major new release under a new name and homepage**
|
||||
`python-oracledb <https://oracle.github.io/python-oracledb/>`__.
|
||||
|
||||
**New projects should install python-oracledb instead of cx_Oracle.**
|
||||
|
||||
`Continuous Query Notification (CQN)
|
||||
<https://www.oracle.com/pls/topic/lookup?ctx=dblatest&
|
||||
id=GUID-373BAF72-3E63-42FE-8BEA-8A2AEFBF1C35>`__ allows applications to receive
|
||||
notifications when a table changes, such as when rows have been updated,
|
||||
regardless of the user or the application that made the change. This can be
|
||||
useful in many circumstances, such as near real-time monitoring, auditing
|
||||
applications, or for such purposes as mid-tier cache invalidation. A cache
|
||||
might hold some values that depend on data in a table. If the data in the
|
||||
table changes, the cached values must then be updated with the new information.
|
||||
|
||||
CQN notification behavior is widely configurable. Choices include specifying
|
||||
what types of SQL should trigger a notification, whether notifications should
|
||||
survive database loss, and control over unsubscription. You can also choose
|
||||
whether notification messages will include ROWIDs of affected rows.
|
||||
|
||||
By default, object-level notification (previously known as Database Change
|
||||
Notification) occurs. With this mode a Python notification method is invoked
|
||||
whenever a database transaction is committed that changes an object referenced
|
||||
by a registered query. However if the :meth:`subscription
|
||||
<Connection.subscribe>` option ``qos`` is :data:`cx_Oracle.SUBSCR_QOS_QUERY`
|
||||
then query-level notification occurs. In this mode, the database notifies the
|
||||
application whenever a committed transaction changes the result of a registered
|
||||
query.
|
||||
|
||||
CQN is best used to track infrequent data changes.
|
||||
|
||||
|
||||
Requirements
|
||||
============
|
||||
|
||||
Before using CQN, users must have appropriate permissions:
|
||||
|
||||
.. code-block:: sql
|
||||
|
||||
GRANT CHANGE NOTIFICATION TO <user-name>;
|
||||
|
||||
To use CQN, connections must have ``events`` mode set to ``True``, for
|
||||
example:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
connection = cx_Oracle.connect(user=user, password=password,
|
||||
dsn="dbhost.example.com/orclpdb1",
|
||||
events=True)
|
||||
|
||||
The default CQN connection mode means the database must be able to connect back
|
||||
to the application using cx_Oracle in order to receive notification events.
|
||||
Alternatively, when using Oracle Database and Oracle client libraries 19.4, or
|
||||
later, subscriptions can set the optional ``client_initiated`` parameter to
|
||||
``True``, see ``Connection.subscribe()`` below.
|
||||
|
||||
The default CQN connection mode typically means that the machine running
|
||||
cx_Oracle needs a fixed IP address. Note :meth:`Connection.subscribe()` does
|
||||
not verify that this reverse connection is possible. If there is any problem
|
||||
sending a notification, then the callback method will not be invoked.
|
||||
Configuration options can include an IP address and port on which cx_Oracle will
|
||||
listen for notifications; otherwise, the database chooses values.
|
||||
|
||||
|
||||
Creating a Subscription
|
||||
=======================
|
||||
|
||||
Subscriptions allow Python to receives notifications for events that take place
|
||||
in the database that match the given parameters.
|
||||
|
||||
For example, a basic CQN subscription might be created like:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
connection.subscribe(callback=my_callback)
|
||||
|
||||
See :meth:`Connection.subscribe()` for details on all of the parameters.
|
||||
|
||||
See :ref:`cqn-operation-codes` for the types of operations that are supported.
|
||||
|
||||
See :ref:`subscr-qos` for the quality of service values that are supported.
|
||||
|
||||
See :ref:`subscr-namespaces` and :ref:`subscr-protocols` for the namespaces and
|
||||
protocols that are supported.
|
||||
|
||||
See :ref:`subscrobj` for more details on the subscription object that is
|
||||
created.
|
||||
|
||||
When using Oracle Database and Oracle client libraries 19.4, or later, the
|
||||
optional subscription parameter ``client_initiated`` can be set:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
connection.subscribe(callback=my_callback, client_initiated=True)
|
||||
|
||||
This enables CQN "client initiated" connections which internally use the same
|
||||
approach as normal cx_Oracle connections to the database, and do not require the
|
||||
database to be able to connect back to the application. Since client initiated
|
||||
connections do not need special network configuration they have ease-of-use and
|
||||
security advantages.
|
||||
|
||||
|
||||
Registering Queries
|
||||
===================
|
||||
|
||||
Once a subscription has been created, one or more queries must be registered by
|
||||
calling :meth:`Subscription.registerquery()`. Registering a query behaves
|
||||
similarly to :meth:`Cursor.execute()`, but only queries are permitted and the
|
||||
``args`` parameter must be a sequence or dictionary.
|
||||
|
||||
An example script to receive query notifications when the 'REGIONS' table data
|
||||
changes is:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
def cqn_callback(message):
|
||||
print("Notification:")
|
||||
for query in message.queries:
|
||||
for tab in query.tables:
|
||||
print("Table:", tab.name)
|
||||
print("Operation:", tab.operation)
|
||||
for row in tab.rows:
|
||||
if row.operation & cx_Oracle.OPCODE_INSERT:
|
||||
print("INSERT of rowid:", row.rowid)
|
||||
if row.operation & cx_Oracle.OPCODE_DELETE:
|
||||
print("DELETE of rowid:", row.rowid)
|
||||
|
||||
subscr = connection.subscribe(callback=cqn_callback,
|
||||
operations=cx_Oracle.OPCODE_INSERT | cx_Oracle.OPCODE_DELETE,
|
||||
qos=cx_Oracle.SUBSCR_QOS_QUERY | cx_Oracle.SUBSCR_QOS_ROWIDS)
|
||||
|
||||
subscr.registerquery("select * from regions")
|
||||
input("Hit enter to stop CQN demo\n")
|
||||
|
||||
Running the above script, shows the initial output as::
|
||||
|
||||
Hit enter to stop CQN demo
|
||||
|
||||
Use SQL*Plus or another tool to commit a change to the table:
|
||||
|
||||
.. code-block:: sql
|
||||
|
||||
insert into regions values(120, 'L');
|
||||
commit;
|
||||
|
||||
When the commit is executed, a notification will be received by the callback
|
||||
which should print something like the following::
|
||||
|
||||
Hit enter to stop CQN demo
|
||||
Notification:
|
||||
Table: HR.REGIONS
|
||||
Operation: 2
|
||||
INSERT of rowid: AAA7EsAAHAAAFS/AAA
|
||||
|
||||
See `GitHub Samples
|
||||
<https://github.com/oracle/python-cx_Oracle/blob/main/samples/cqn.py>`__
|
||||
for a runnable CQN example.
|
|
@ -1,47 +0,0 @@
|
|||
.. _exception:
|
||||
|
||||
******************
|
||||
Exception Handling
|
||||
******************
|
||||
|
||||
.. note::
|
||||
|
||||
**cx_Oracle has a major new release under a new name and homepage**
|
||||
`python-oracledb <https://oracle.github.io/python-oracledb/>`__.
|
||||
|
||||
**New projects should install python-oracledb instead of cx_Oracle.**
|
||||
|
||||
All exceptions raised by cx_Oracle are inherited from :attr:`cx_Oracle.Error`.
|
||||
See :ref:`Exceptions <exceptions>` for more details on the various exceptions
|
||||
defined by cx_Oracle. See the exception handling section in the
|
||||
:ref:`API manual <exchandling>` for more details on the information available
|
||||
when an exception is raised.
|
||||
|
||||
Applications can catch exceptions as needed. For example, when trying to add a
|
||||
customer that already exists in the database, the following could be used
|
||||
to catch the exception:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
try:
|
||||
cursor.execute("insert into customer values (101, 'Customer A')")
|
||||
except cx_Oracle.IntegrityError:
|
||||
print("Customer ID already exists")
|
||||
else:
|
||||
print("Customer added")
|
||||
|
||||
|
||||
If information about the exception needs to be processed instead, the following
|
||||
code can be used:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
try:
|
||||
cursor.execute("insert into customer values (101, 'Customer A')")
|
||||
except cx_Oracle.IntegrityError as e:
|
||||
error_obj, = e.args
|
||||
print("Customer ID already exists")
|
||||
print("Error Code:", error_obj.code)
|
||||
print("Error Message:", error_obj.message)
|
||||
else:
|
||||
print("Customer added")
|
|
@ -1,164 +0,0 @@
|
|||
.. _globalization:
|
||||
|
||||
********************************
|
||||
Character Sets and Globalization
|
||||
********************************
|
||||
|
||||
.. note::
|
||||
|
||||
**cx_Oracle has a major new release under a new name and homepage**
|
||||
`python-oracledb <https://oracle.github.io/python-oracledb/>`__.
|
||||
|
||||
**New projects should install python-oracledb instead of cx_Oracle.**
|
||||
|
||||
Data fetched from, and sent to, Oracle Database will be mapped between the
|
||||
database character set and the "Oracle client" character set of the Oracle
|
||||
Client libraries used by cx_Oracle. If data cannot be correctly mapped between
|
||||
client and server character sets, then it may be corrupted or queries may fail
|
||||
with :ref:`"codec can't decode byte" <codecerror>`.
|
||||
|
||||
cx_Oracle uses Oracle’s National Language Support (NLS) to assist in
|
||||
globalizing applications. As well as character set support, there are many
|
||||
other features that will be useful in applications. See the
|
||||
`Database Globalization Support Guide
|
||||
<https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=NLSPG>`__.
|
||||
|
||||
|
||||
Setting the Client Character Set
|
||||
================================
|
||||
|
||||
In cx_Oracle 8 the default encoding used for all character data changed to
|
||||
"UTF-8". This universal encoding is suitable for most applications. If you
|
||||
have a special need, you can pass the ``encoding`` and ``nencoding`` parameters
|
||||
to the :meth:`cx_Oracle.connect` and :meth:`cx_Oracle.SessionPool` methods to
|
||||
specify different Oracle Client character sets. For example:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import cx_Oracle
|
||||
connection = cx_Oracle.connect(dsn=connect_string, encoding="US-ASCII",
|
||||
nencoding="UTF-8")
|
||||
|
||||
.. note::
|
||||
|
||||
In a future release of cx_Oracle, only UTF-8 will be supported.
|
||||
|
||||
The ``encoding`` parameter affects character data such as VARCHAR2 and CLOB
|
||||
columns. The ``nencoding`` parameter affects "National Character" data such as
|
||||
NVARCHAR2 and NCLOB. If you are not using national character types, then you
|
||||
can omit ``nencoding``. Both the ``encoding`` and ``nencoding`` parameters are
|
||||
expected to be one of the `Python standard encodings
|
||||
<https://docs.python.org/3/library/codecs.html#standard-encodings>`__ such as
|
||||
``UTF-8``. Do not accidentally use ``UTF8``, which Oracle uses to specify the
|
||||
older Unicode 3.0 Universal character set, ``CESU-8``. Note that Oracle does
|
||||
not recognize all of the encodings that Python recognizes. You can see which
|
||||
encodings are usable in cx_Oracle by issuing this query:
|
||||
|
||||
.. code-block:: sql
|
||||
|
||||
select distinct utl_i18n.map_charset(value)
|
||||
from v$nls_valid_values
|
||||
where parameter = 'CHARACTERSET'
|
||||
and utl_i18n.map_charset(value) is not null
|
||||
order by 1
|
||||
|
||||
.. note::
|
||||
|
||||
From cx_Oracle 8, it is no longer possible to change the character set
|
||||
using the ``NLS_LANG`` environment variable. The character set component
|
||||
of that variable is ignored. The language and territory components of
|
||||
``NLS_LANG`` are still respected by the Oracle Client libraries.
|
||||
|
||||
Character Set Example
|
||||
---------------------
|
||||
|
||||
The script below tries to display data containing a Euro symbol from the
|
||||
database.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
connection = cx_Oracle.connect(user=user, password=password,
|
||||
dsn="dbhost.example.com/orclpdb1",
|
||||
encoding="US-ASCII")
|
||||
cursor = connection.cursor()
|
||||
for row in cursor.execute("select nvarchar2_column from nchar_test"):
|
||||
print(row)
|
||||
|
||||
Because the '€' symbol is not supported by the ``US-ASCII`` character set, all
|
||||
'€' characters are replaced by '¿' in the cx_Oracle output::
|
||||
|
||||
('¿',)
|
||||
|
||||
When the ``encoding`` parameter is removed (or set to "UTF-8") during
|
||||
connection:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
connection = cx_Oracle.connect(user=user, password=password,
|
||||
dsn="dbhost.example.com/orclpdb1")
|
||||
|
||||
Then the output displays the Euro symbol as desired::
|
||||
|
||||
('€',)
|
||||
|
||||
.. _findingcharset:
|
||||
|
||||
Finding the Database and Client Character Set
|
||||
---------------------------------------------
|
||||
|
||||
To find the database character set, execute the query:
|
||||
|
||||
.. code-block:: sql
|
||||
|
||||
SELECT value AS db_charset
|
||||
FROM nls_database_parameters
|
||||
WHERE parameter = 'NLS_CHARACTERSET';
|
||||
|
||||
To find the database 'national character set' used for NCHAR and related types,
|
||||
execute the query:
|
||||
|
||||
.. code-block:: sql
|
||||
|
||||
SELECT value AS db_ncharset
|
||||
FROM nls_database_parameters
|
||||
WHERE parameter = 'NLS_NCHAR_CHARACTERSET';
|
||||
|
||||
To find the current "client" character set used by cx_Oracle, execute the
|
||||
query:
|
||||
|
||||
.. code-block:: sql
|
||||
|
||||
SELECT DISTINCT client_charset AS client_charset
|
||||
FROM v$session_connect_info
|
||||
WHERE sid = SYS_CONTEXT('USERENV', 'SID');
|
||||
|
||||
If these character sets do not match, characters transferred over Oracle Net
|
||||
will be mapped from one character set to another. This may impact performance
|
||||
and may result in invalid data.
|
||||
|
||||
Setting the Oracle Client Locale
|
||||
================================
|
||||
|
||||
You can use the ``NLS_LANG`` environment variable to set the language and
|
||||
territory used by the Oracle Client libraries. For example, on Linux you could
|
||||
set::
|
||||
|
||||
export NLS_LANG=JAPANESE_JAPAN
|
||||
|
||||
The language ("JAPANESE" in this example) specifies conventions such as the
|
||||
language used for Oracle Database messages, sorting, day names, and month
|
||||
names. The territory ("JAPAN") specifies conventions such as the default date,
|
||||
monetary, and numeric formats. If the language is not specified, then the value
|
||||
defaults to AMERICAN. If the territory is not specified, then the value is
|
||||
derived from the language value. See `Choosing a Locale with the NLS_LANG
|
||||
Environment Variable
|
||||
<https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-86A29834-AE29-4BA5-8A78-E19C168B690A>`__
|
||||
|
||||
If the ``NLS_LANG`` environment variable is set in the application with
|
||||
``os.environ['NLS_LANG']``, it must be set before any connection pool is
|
||||
created, or before any standalone connections are created.
|
||||
|
||||
Other Oracle globalization variables, such as ``NLS_DATE_FORMAT`` can also be
|
||||
set to change the behavior of cx_Oracle, see `Setting NLS Parameters
|
||||
<https://www.oracle.com/pls/topic/lookup?ctx=dblatest&
|
||||
id=GUID-6475CA50-6476-4559-AD87-35D431276B20>`__.
|
|
@ -1,223 +0,0 @@
|
|||
.. _highavailability:
|
||||
|
||||
********************************
|
||||
High Availability with cx_Oracle
|
||||
********************************
|
||||
|
||||
.. note::
|
||||
|
||||
**cx_Oracle has a major new release under a new name and homepage**
|
||||
`python-oracledb <https://oracle.github.io/python-oracledb/>`__.
|
||||
|
||||
**New projects should install python-oracledb instead of cx_Oracle.**
|
||||
|
||||
Applications can utilize many features for high availability (HA) during planned and
|
||||
unplanned outages in order to:
|
||||
|
||||
* Reduce application downtime
|
||||
* Eliminate compromises between high availability and performance
|
||||
* Increase operational productivity
|
||||
|
||||
.. _harecommend:
|
||||
|
||||
General HA Recommendations
|
||||
--------------------------
|
||||
|
||||
General recommendations for creating highly available cx_Oracle programs are:
|
||||
|
||||
* Tune operating system and Oracle Network parameters to avoid long TCP timeouts, to prevent firewalls killing connections, and to avoid connection storms.
|
||||
* Implement application error handling and recovery.
|
||||
* Use the most recent version of the Oracle client libraries. New versions have improvements to features such as dead database server detection, and make it easier to set connection options.
|
||||
* Use the most recent version of Oracle Database. New database versions introduce, and enhance, features such as Application Continuity (AC) and Transparent Application Continuity (TAC).
|
||||
* Utilize Oracle Database technologies such as `RAC <https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=RACAD>`__ or standby databases.
|
||||
* Configure database services to emit :ref:`FAN <fan>` events.
|
||||
* Use a :ref:`connection pool <connpool>`, because pools can handle database events and take proactive and corrective action for draining, run time load balancing, and fail over. Set the minimum and maximum pool sizes to the same values to avoid connection storms. Remove resource manager or user profiles that prematurely close sessions.
|
||||
* Test all scenarios thoroughly.
|
||||
|
||||
.. _hanetwork:
|
||||
|
||||
Network Configuration
|
||||
---------------------
|
||||
|
||||
The operating system TCP and :ref:`Oracle Net configuration <optnetfiles>`
|
||||
should be configured for performance and availability.
|
||||
|
||||
Options such as `SQLNET.OUTBOUND_CONNECT_TIMEOUT
|
||||
<https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-0857C817-675F-4CF0-BFBB-C3667F119176>`__,
|
||||
`SQLNET.RECV_TIMEOUT
|
||||
<https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-4A19D81A-75F0-448E-B271-24E5187B5909>`__
|
||||
and `SQLNET.SEND_TIMEOUT
|
||||
<https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-48547756-9C0B-4D14-BE85-E7ADDD1A3A66>`__
|
||||
can be explored.
|
||||
|
||||
`Oracle Net Services
|
||||
<https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=NETRF>`__ options may
|
||||
also be useful for high availability and performance tuning. For example the
|
||||
database's `listener.ora` file can have `RATE_LIMIT
|
||||
<https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-F302BF91-64F2-4CE8-A3C7-9FDB5BA6DCF8>`__
|
||||
and `QUEUESIZE
|
||||
<https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-FF87387C-1779-4CC3-932A-79BB01391C28>`__
|
||||
parameters that can help handle connection storms.
|
||||
|
||||
With Oracle Client 19c, `EXPIRE_TIME
|
||||
<https://docs.oracle.com/en/database/oracle/oracle-database/20/netrf/local-naming-parameters-in-tns-ora-file.html#GUID-6140611A-83FC-4C9C-B31F-A41FC2A5B12D>`__
|
||||
can be used in :ref:`tnsnames.ora <optnetfiles>` connect descriptors to prevent
|
||||
firewalls from terminating idle connections and to adjust keepalive timeouts.
|
||||
The general recommendation for ``EXPIRE_TIME`` is to use a value that is
|
||||
slightly less than half of the termination period. In older versions of Oracle
|
||||
Client, a ``tnsnames.ora`` connect descriptor option `ENABLE=BROKEN
|
||||
<https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-7A18022A-E40D-4880-B3CE-7EE9864756CA>`_
|
||||
can be used instead of ``EXPIRE_TIME``. These settings can also aid detection
|
||||
of a terminated remote database server.
|
||||
|
||||
When cx_Oracle uses :ref:`Oracle Client libraries 19c <archfig>`, then the
|
||||
:ref:`Easy Connect Plus syntax <easyconnect>` syntax enables some options to be
|
||||
used without needing a ``sqlnet.ora`` file. For example, if your firewall times
|
||||
out every 4 minutes, and you cannot alter the firewall settings, then you may
|
||||
decide to use ``EXPIRE_TIME`` in your connect string to send a probe every 2
|
||||
minutes to the database to keep connections 'alive'::
|
||||
|
||||
connection = cx_Oracle.connect("hr", userpwd, "dbhost.example.com/orclpdb1?expire_time=2")
|
||||
|
||||
.. _fan:
|
||||
|
||||
Fast Application Notification (FAN)
|
||||
-----------------------------------
|
||||
|
||||
Users of `Oracle Database FAN
|
||||
<https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-F3FBE48B-468B-4393-8B0C-D5C8E0E4374D>`__
|
||||
must connect to a FAN-enabled database service. The application should have
|
||||
``events`` set to True when connecting. This value can also be changed via
|
||||
:ref:`Oracle Client Configuration <optclientfiles>`.
|
||||
|
||||
FAN support is useful for planned and unplanned outages. It provides immediate
|
||||
notification to cx_Oracle following outages related to the database, computers,
|
||||
and networks. Without FAN, cx_Oracle can hang until a TCP timeout occurs and an
|
||||
error is returned, which might be several minutes.
|
||||
|
||||
FAN allows cx_Oracle to provide high availability features without the
|
||||
application being aware of an outage. Unused, idle connections in a
|
||||
:ref:`connection pool <connpool>` will be automatically cleaned up. A future
|
||||
:meth:`SessionPool.acquire()` call will establish a fresh connection to a
|
||||
surviving database instance without the application being aware of any service
|
||||
disruption.
|
||||
|
||||
To handle errors that affect active connections, you can add application logic
|
||||
to re-connect (this will connect to a surviving database instance) and replay
|
||||
application logic without having to return an error to the application user.
|
||||
|
||||
FAN benefits users of Oracle Database's clustering technology `Oracle RAC
|
||||
<https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-D04AA2A7-2E68-4C5C-BD6E-36C62427B98E>`__
|
||||
because connections to surviving database instances can be immediately made.
|
||||
Users of Oracle's Data Guard with a broker will get FAN events generated when
|
||||
the standby database goes online. Standalone databases will send FAN events
|
||||
when the database restarts.
|
||||
|
||||
For a more information on FAN see the `white paper on Fast Application
|
||||
Notification
|
||||
<https://www.oracle.com/technetwork/database/options/clustering/applicationcontinuity/learnmore/fastapplicationnotification12c-2538999.pdf>`__.
|
||||
|
||||
.. _appcont:
|
||||
|
||||
Application Continuity (AC)
|
||||
---------------------------
|
||||
|
||||
Oracle Application Continuity and Transparent Application Continuity are Oracle
|
||||
Database technologies that record application interaction with the database and,
|
||||
in the event of a database instance outage, attempt to replay the interaction on
|
||||
a surviving database instance. If successful, users will be unaware of any
|
||||
database issue. AC and TAC are best suited for OLTP applications.
|
||||
|
||||
When AC or TAC are configured on the database service, they are transparently
|
||||
available to cx_Oracle applications.
|
||||
|
||||
You must thoroughly test your application because not all lower level calls in
|
||||
the cx_Oracle implementation can be replayed.
|
||||
|
||||
See `OCI and Application Continuity
|
||||
<https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-A8DD9422-2F82-42A9-9555-134296416E8F>`__
|
||||
for more information.
|
||||
|
||||
.. _tg:
|
||||
|
||||
Transaction Guard
|
||||
-----------------
|
||||
|
||||
cx_Oracle supports `Transaction Guard
|
||||
<https://www.oracle.com/pls/topic/lookup?ctx=dblatest&
|
||||
id=GUID-A675AF7B-6FF0-460D-A6E6-C15E7C328C8F>`__ which enables Python
|
||||
application to verify the success or failure of the last transaction in the
|
||||
event of an unplanned outage. This feature is available when both client and
|
||||
database are 12.1 or higher.
|
||||
|
||||
Using Transaction Guard helps to:
|
||||
|
||||
* Preserve the commit outcome
|
||||
* Ensure a known outcome for every transaction
|
||||
|
||||
See `Oracle Database Development Guide
|
||||
<https://www.oracle.com/pls/topic/lookup?ctx=dblatest&
|
||||
id=GUID-6C5880E5-C45F-4858-A069-A28BB25FD1DB>`__ for more information about
|
||||
using Transaction Guard.
|
||||
|
||||
When an error occurs during commit, the Python application can acquire the
|
||||
logical transaction id (``ltxid``) from the connection and then call a
|
||||
procedure to determine the outcome of the commit for this logical transaction
|
||||
id.
|
||||
|
||||
Follow the steps below to use the Transaction Guard feature in Python:
|
||||
|
||||
1. Grant execute privileges to the database users who will be checking the
|
||||
outcome of the commit. Login as SYSDBA and run the following command:
|
||||
|
||||
.. code-block:: sql
|
||||
|
||||
GRANT EXECUTE ON DBMS_APP_CONT TO <username>;
|
||||
|
||||
2. Create a new service by executing the following PL/SQL block as SYSDBA.
|
||||
Replace the ``<service-name>``, ``<network-name>`` and
|
||||
``<retention-value>`` values with suitable values. It is important that the
|
||||
``COMMIT_OUTCOME`` parameter be set to true for Transaction Guard to
|
||||
function properly.
|
||||
|
||||
.. code-block:: sql
|
||||
|
||||
DECLARE
|
||||
t_Params dbms_service.svc_parameter_array;
|
||||
BEGIN
|
||||
t_Params('COMMIT_OUTCOME') := 'true';
|
||||
t_Params('RETENTION_TIMEOUT') := <retention-value>;
|
||||
DBMS_SERVICE.CREATE_SERVICE('<service-name>', '<network-name>', t_Params);
|
||||
END;
|
||||
/
|
||||
|
||||
3. Start the service by executing the following PL/SQL block as SYSDBA:
|
||||
|
||||
.. code-block:: sql
|
||||
|
||||
BEGIN
|
||||
DBMS_SERVICE.start_service('<service-name>');
|
||||
END;
|
||||
/
|
||||
|
||||
Ensure the service is running by examining the output of the following query:
|
||||
|
||||
.. code-block:: sql
|
||||
|
||||
SELECT name, network_name FROM V$ACTIVE_SERVICES ORDER BY 1;
|
||||
|
||||
|
||||
**Python Application code requirements to use Transaction Guard**
|
||||
|
||||
In the Python application code:
|
||||
|
||||
* Use the connection attribute :attr:`~Connection.ltxid` to determine the
|
||||
logical transaction id.
|
||||
* Call the ``DBMS_APP_CONT.GET_LTXID_OUTCOME`` PL/SQL procedure with the
|
||||
logical transaction id acquired from the connection attribute. This returns
|
||||
a boolean value indicating if the last transaction was committed and whether
|
||||
the last call was completed successfully or not.
|
||||
|
||||
See the `Transaction Guard Sample
|
||||
<https://github.com/oracle/python-cx_Oracle/blob/main/
|
||||
samples/transaction_guard.py>`__ for further details.
|
|
@ -1,415 +0,0 @@
|
|||
.. _initialization:
|
||||
|
||||
**************************
|
||||
cx_Oracle 8 Initialization
|
||||
**************************
|
||||
|
||||
.. note::
|
||||
|
||||
**cx_Oracle has a major new release under a new name and homepage**
|
||||
`python-oracledb <https://oracle.github.io/python-oracledb/>`__.
|
||||
|
||||
**New projects should install python-oracledb instead of cx_Oracle.**
|
||||
|
||||
The cx_Oracle module loads Oracle Client libraries which communicate over
|
||||
Oracle Net to an existing database. The Oracle Client libraries need to be
|
||||
installed separately. See :ref:`installation`. Oracle Net is not a separate
|
||||
product: it is how the Oracle Client and Oracle Database communicate.
|
||||
|
||||
.. figure:: /images/cx_Oracle_arch.png
|
||||
|
||||
cx_Oracle Architecture
|
||||
|
||||
.. _libinit:
|
||||
|
||||
Locating the Oracle Client Libraries
|
||||
====================================
|
||||
|
||||
cx_Oracle dynamically loads the Oracle Client libraries using a search
|
||||
heuristic. Only the first set of libraries found are loaded. The libraries
|
||||
can be in an installation of Oracle Instant Client, in a full Oracle Client
|
||||
installation, or in an Oracle Database installation (if Python is running on
|
||||
the same machine as the database). The versions of Oracle Client 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>`__.
|
||||
|
||||
|
||||
.. _wininit:
|
||||
|
||||
* On Windows, cx_Oracle looks for the Oracle Client libraries as follows:
|
||||
|
||||
- In the ``lib_dir`` directory specified in a call to
|
||||
:meth:`cx_Oracle.init_oracle_client()`. 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
|
||||
software installation, otherwise files such as message files will not be
|
||||
located. On Windows when the path contains backslashes, use a 'raw'
|
||||
string like ``lib_dir=r"C:\instantclient_19_6"``. If the Oracle Client
|
||||
libraries cannot be loaded from ``lib_dir``, then an exception is raised.
|
||||
|
||||
- If ``lib_dir`` was not specified, then Oracle Client libraries are looked
|
||||
for in the directory where the cx_Oracle 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,
|
||||
no exception is raised and the search continues, see next bullet point.
|
||||
|
||||
- In the directories on the system library search path, e.g. the ``PATH``
|
||||
environment variable. If the Oracle Client libraries cannot be loaded,
|
||||
then an exception is raised.
|
||||
|
||||
.. _macinit:
|
||||
|
||||
* On macOS, cx_Oracle looks for the Oracle Client libraries as follows:
|
||||
|
||||
- In the ``lib_dir`` directory specified in a call to
|
||||
:meth:`cx_Oracle.init_oracle_client()`. 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.
|
||||
|
||||
- If ``lib_dir`` was not specified, then Oracle Client libraries are looked
|
||||
for in the directory where the cx_Oracle binary module is. This directory
|
||||
should contain the libraries from an unzipped Instant Client 'Basic' or
|
||||
'Basic Light' package. For example if
|
||||
``/Users/your_username/Library/Python/3.8/lib/python/site-packages``
|
||||
contains ``cx_Oracle.cpython-38-darwin.so``, then you could run ``ln -s
|
||||
~/instantclient_19_3/libclntsh.dylib
|
||||
~/Library/Python/3.8/lib/python/site-packages``. If the libraries are not
|
||||
found, no exception is raised and the search continues, see next bullet
|
||||
point.
|
||||
|
||||
- In the directories on the system library search path, e.g. ``~/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:
|
||||
|
||||
* On Linux and related platforms, cx_Oracle looks for the Oracle Client
|
||||
libraries as follows:
|
||||
|
||||
- In the ``lib_dir`` directory specified in a call to
|
||||
:meth:`cx_Oracle.init_oracle_client()`.
|
||||
|
||||
**Note this is only useful to force immediate loading of the libraries
|
||||
because on Linux and related platforms the libraries must always be in the
|
||||
system library search path**.
|
||||
|
||||
The ``lib_dir`` 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
|
||||
the ``ORACLE_HOME`` environment variable. If the Oracle Client libraries
|
||||
cannot be loaded from ``lib_dir``, then an exception is raised.
|
||||
|
||||
- If ``lib_dir`` was not specified, then 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 the libraries
|
||||
are not found, no exception is raised and the search continues, see next
|
||||
bullet point.
|
||||
|
||||
- In ``$ORACLE_HOME/lib``. Note the environment variable ``ORACLE_HOME``
|
||||
should only ever be set when you have a full database installation or
|
||||
full client installation. 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.
|
||||
|
||||
If you call :meth:`cx_Oracle.init_oracle_client()` with a ``lib_dir``
|
||||
parameter, the Oracle Client libraries are loaded immediately from that
|
||||
directory. If you call :meth:`cx_Oracle.init_oracle_client()` but do *not* set
|
||||
the ``lib_dir`` parameter, the Oracle Client libraries are loaded immediately
|
||||
using the search heuristic above. If you do not call
|
||||
:meth:`cx_Oracle.init_oracle_client()`, then the libraries are loaded using the
|
||||
search heuristic when the first cx_Oracle function that depends on the
|
||||
libraries is called, for example when a connection pool is created. If there
|
||||
is a problem loading the libraries, then an exception is raised.
|
||||
|
||||
Make sure the Python process has directory and file access permissions for the
|
||||
Oracle Client libraries. On Linux ensure a ``libclntsh.so`` file exists. On
|
||||
macOS ensure a ``libclntsh.dylib`` file exists. cx_Oracle will not directly
|
||||
load ``libclntsh.*.XX.1`` files in ``lib_dir`` or from the directory where the
|
||||
cx_Oracle binary module is. Note other libraries used by ``libclntsh*`` are
|
||||
also required.
|
||||
|
||||
To trace the loading of Oracle Client libraries, the environment variable
|
||||
``DPI_DEBUG_LEVEL`` can be set to 64 before starting Python. For example, on
|
||||
Linux, you might use::
|
||||
|
||||
$ export DPI_DEBUG_LEVEL=64
|
||||
$ python myapp.py 2> log.txt
|
||||
|
||||
|
||||
.. _usinginitoracleclient:
|
||||
|
||||
Using cx_Oracle.init_oracle_client() to set the Oracle Client directory
|
||||
-----------------------------------------------------------------------
|
||||
|
||||
Applications can call the function :meth:`cx_Oracle.init_oracle_client()` to
|
||||
specify the directory containing Oracle Instant Client libraries. The Oracle
|
||||
Client Libraries are loaded when ``init_oracle_client()`` is called. For
|
||||
example, if the Oracle Instant Client Libraries are in
|
||||
``C:\oracle\instantclient_19_9`` on Windows or
|
||||
``$HOME/Downloads/instantclient_19_8`` on macOS, then you can use:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import cx_Oracle
|
||||
import sys
|
||||
import os
|
||||
|
||||
try:
|
||||
if sys.platform.startswith("darwin"):
|
||||
lib_dir = os.path.join(os.environ.get("HOME"), "Downloads",
|
||||
"instantclient_19_8")
|
||||
cx_Oracle.init_oracle_client(lib_dir=lib_dir)
|
||||
elif sys.platform.startswith("win32"):
|
||||
lib_dir=r"C:\oracle\instantclient_19_9"
|
||||
cx_Oracle.init_oracle_client(lib_dir=lib_dir)
|
||||
except Exception as err:
|
||||
print("Whoops!")
|
||||
print(err)
|
||||
sys.exit(1)
|
||||
|
||||
Note the use of a 'raw' string ``r"..."`` on Windows so that backslashes are
|
||||
treated as directory separators.
|
||||
|
||||
The :meth:`~cx_Oracle.init_oracle_client()` function can only be called once.
|
||||
|
||||
**Note 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, 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,
|
||||
and other Oracle environment variables, before starting Python, as
|
||||
shown in :ref:`envset`.
|
||||
|
||||
.. _optnetfiles:
|
||||
|
||||
Optional Oracle Net Configuration Files
|
||||
=======================================
|
||||
|
||||
Optional Oracle Net configuration files are read when cx_Oracle is loaded.
|
||||
These files affect connections and applications. The common files are:
|
||||
|
||||
* ``tnsnames.ora``: A configuration file that defines databases addresses
|
||||
for establishing connections. See :ref:`Net Service Name for Connection
|
||||
Strings <netservice>`.
|
||||
|
||||
* ``sqlnet.ora``: A profile configuration file that may contain information
|
||||
on features such as connection failover, network encryption, logging, and
|
||||
tracing. See `Oracle Net Services Reference
|
||||
<https://www.oracle.com/pls/topic/lookup?ctx=dblatest&
|
||||
id=GUID-19423B71-3F6C-430F-84CC-18145CC2A818>`__ for more information.
|
||||
|
||||
The files should be in a directory accessible to Python, not on the database
|
||||
server host.
|
||||
|
||||
For example, if the file ``/etc/my-oracle-config/tnsnames.ora`` should be used,
|
||||
you can call :meth:`cx_Oracle.init_oracle_client()`:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import cx_Oracle
|
||||
import sys
|
||||
|
||||
try:
|
||||
cx_Oracle.init_oracle_client(config_dir="/etc/my-oracle-config")
|
||||
except Exception as err:
|
||||
print("Whoops!")
|
||||
print(err)
|
||||
sys.exit(1)
|
||||
|
||||
This is equivalent to setting the environment variable `TNS_ADMIN
|
||||
<https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-12C94B15-2CE1-4B98-9D0C-8226A9DDF4CB>`__
|
||||
to ``/etc/my-oracle-config``.
|
||||
|
||||
If :meth:`~cx_Oracle.init_oracle_client()` is not called, or it is called but
|
||||
``config_dir`` is not specified, then default directories searched for the
|
||||
configuration files. They include:
|
||||
|
||||
* ``$TNS_ADMIN``
|
||||
* ``/opt/oracle/instantclient_19_6/network/admin`` if Instant Client is in ``/opt/oracle/instantclient_19_6``.
|
||||
* ``/usr/lib/oracle/19.6/client64/lib/network/admin`` if Oracle 19.6 Instant Client RPMs are used on Linux.
|
||||
* ``$ORACLE_HOME/network/admin`` if cx_Oracle is using libraries from a database installation.
|
||||
|
||||
A wallet configuration file ``cwallet.sso`` for secure connection can be
|
||||
located with, or separately from, the ``tnsnames.ora`` and ``sqlnet.ora``
|
||||
files. It should be securely stored. The ``sqlnet.ora`` file's
|
||||
``WALLET_LOCATION`` path should be set to the directory containing
|
||||
``cwallet.sso``. For Oracle Autonomous Database use of wallets, see
|
||||
:ref:`autonomousdb`.
|
||||
|
||||
Note the :ref:`easyconnect` can set many common configuration options without
|
||||
needing ``tnsnames.ora`` or ``sqlnet.ora`` files.
|
||||
|
||||
The section :ref:`Network Configuration <hanetwork>` has some discussion about
|
||||
Oracle Net configuration.
|
||||
|
||||
.. _optclientfiles:
|
||||
|
||||
Optional Oracle Client Configuration Files
|
||||
==========================================
|
||||
|
||||
When cx_Oracle uses Oracle Client libraries version 12.1, or later, an optional
|
||||
client parameter file called ``oraaccess.xml`` can be used to configure some
|
||||
behviors 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`_.
|
||||
|
||||
A sample ``oraaccess.xml`` file that sets the Oracle client ‘prefetch’ value to
|
||||
1000 rows. This value affects every SQL query in the application::
|
||||
|
||||
<?xml version="1.0"?>
|
||||
<oraaccess xmlns="http://xmlns.oracle.com/oci/oraaccess"
|
||||
xmlns:oci="http://xmlns.oracle.com/oci/oraaccess"
|
||||
schemaLocation="http://xmlns.oracle.com/oci/oraaccess
|
||||
http://xmlns.oracle.com/oci/oraaccess.xsd">
|
||||
<default_parameters>
|
||||
<prefetch>
|
||||
<rows>1000</rows>
|
||||
</prefetch>
|
||||
</default_parameters>
|
||||
</oraaccess>
|
||||
|
||||
Prefetching is the number of additional rows the underlying Oracle client
|
||||
library fetches whenever cx_Oracle 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 cx_Oracle to the application.
|
||||
The cache management is transparently handled by the Oracle client libraries.
|
||||
Note, standard cx_Oracle fetch tuning is via :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:
|
||||
|
||||
- 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-6E21AA56-5BBE-422A-802C-197CAC8AAEA4>`__
|
||||
|
||||
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.
|
||||
|
||||
.. _envset:
|
||||
|
||||
Oracle Environment Variables
|
||||
============================
|
||||
|
||||
Some common environment variables that influence cx_Oracle are shown below. The
|
||||
variables that may be needed depend on how Python is installed, how you connect
|
||||
to the database, and what optional settings are desired. It is recommended to
|
||||
set Oracle variables in the environment before invoking Python, however they may
|
||||
also be set in the application with ``os.putenv()`` before the first connection
|
||||
is established. System environment variables like ``LD_LIBRARY_PATH`` must be
|
||||
set before Python starts.
|
||||
|
||||
.. list-table:: Common Oracle environment variables
|
||||
:header-rows: 1
|
||||
:widths: 1 2
|
||||
:align: left
|
||||
|
||||
* - Oracle Environment Variables
|
||||
- Purpose
|
||||
* - 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_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``.
|
||||
* - PATH
|
||||
- The library search path for Windows should include the location where
|
||||
``OCI.DLL`` is found. Not needed if you set ``lib_dir`` in a call to
|
||||
:meth:`cx_Oracle.init_oracle_client()`
|
||||
* - TNS_ADMIN
|
||||
- The directory of optional Oracle Client configuration files such as
|
||||
``tnsnames.ora`` and ``sqlnet.ora``. Not needed if the configuration
|
||||
files are in a default location or if ``config_dir`` was not used in
|
||||
:meth:`cx_Oracle.init_oracle_client()`. See :ref:`optnetfiles`.
|
||||
* - ORA_SDTZ
|
||||
- The default session time zone.
|
||||
* - ORA_TZFILE
|
||||
- The name of the Oracle time zone file to use. See below.
|
||||
* - ORACLE_HOME
|
||||
- The directory containing the Oracle Database software. The directory
|
||||
and various configuration files must be readable by the Python process.
|
||||
This variable should not be set if you are using Oracle Instant Client.
|
||||
* - NLS_LANG
|
||||
- Determines the 'national language support' globalization options for
|
||||
cx_Oracle. Note: from cx_Oracle 8, the character set component is
|
||||
ignored and only the language and territory components of ``NLS_LANG``
|
||||
are used. The character set can instead be specified during connection
|
||||
or connection pool creation. See :ref:`globalization`.
|
||||
* - NLS_DATE_FORMAT, NLS_TIMESTAMP_FORMAT
|
||||
- Often set in Python applications to force a consistent date format
|
||||
independent of the locale. The variables are ignored if the environment
|
||||
variable ``NLS_LANG`` is not set.
|
||||
|
||||
Oracle Instant Client includes a small and big time zone file, for example
|
||||
``timezone_32.dat`` and ``timezlrg_32.dat``. The versions can be shown by running
|
||||
the utility ``genezi -v`` located in the Instant Client directory. The small file
|
||||
contains only the most commonly used time zones. By default the larger
|
||||
``timezlrg_n.dat`` file is used. If you want to use the smaller ``timezone_n.dat``
|
||||
file, then set the ``ORA_TZFILE`` environment variable to the name of the file
|
||||
without any directory prefix, for example ``export ORA_TZFILE=timezone_32.dat``.
|
||||
With Oracle Instant Client 12.2 or later, you can also use an external time zone
|
||||
file. Create a subdirectory ``oracore/zoneinfo`` under the Instant Client
|
||||
directory, and move the file into it. Then set ``ORA_TZFILE`` to the file name,
|
||||
without any directory prefix. The ``genezi -v`` utility will show the time zone
|
||||
file in use.
|
||||
|
||||
If cx_Oracle is using Oracle Client libraries from an Oracle Database or full
|
||||
Oracle Client software installation, and you want to use a non-default time zone
|
||||
file, then 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>`__.
|
||||
|
||||
.. _otherinit:
|
||||
|
||||
Other cx_Oracle Initialization
|
||||
==============================
|
||||
|
||||
The :meth:`cx_Oracle.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 cx_Oracle is being used. An example of setting the
|
||||
parameters is:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import cx_Oracle
|
||||
import sys
|
||||
|
||||
try:
|
||||
cx_Oracle.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 the value "cx_Oracle : *version*" is used.
|
||||
|
||||
The ``error_url`` string will be shown in the exception raised if
|
||||
``init_oracle_client()`` cannot load the Oracle Client libraries. This allows
|
||||
applications that use cx_Oracle to refer users to application-specific
|
||||
installation instructions. If this value is not specified, then the
|
||||
:ref:`installation` URL is used.
|
|
@ -1,936 +0,0 @@
|
|||
.. _installation:
|
||||
|
||||
************************
|
||||
cx_Oracle 8 Installation
|
||||
************************
|
||||
|
||||
.. note::
|
||||
|
||||
**cx_Oracle has a major new release under a new name and homepage**
|
||||
`python-oracledb <https://oracle.github.io/python-oracledb/>`__.
|
||||
|
||||
**New projects should install python-oracledb instead of cx_Oracle.**
|
||||
|
||||
Overview
|
||||
========
|
||||
|
||||
To use cx_Oracle 8.3 with Python and Oracle Database you need:
|
||||
|
||||
- Python 3.6 and higher. Older versions of cx_Oracle may work with older
|
||||
versions of Python.
|
||||
|
||||
- Oracle Client libraries. These can be from the free `Oracle Instant Client
|
||||
<https://www.oracle.com/database/technologies/instant-client.html>`__, from a
|
||||
full Oracle Client installation, or from those included in Oracle Database if
|
||||
Python is on the same machine as the database. Oracle client libraries
|
||||
versions 21, 19, 18, 12, and 11.2 are supported where available on Linux,
|
||||
Windows and macOS (Intel x86). Users have also reported success with other
|
||||
platforms. Use the latest client possible: Oracle's standard client-server
|
||||
version interoperability allows connection to both older and newer databases.
|
||||
|
||||
- An Oracle Database, either local or remote.
|
||||
|
||||
The cx_Oracle module loads Oracle Client libraries which communicate
|
||||
over Oracle Net to an existing database. Oracle Net is not a separate
|
||||
product: it is how the Oracle Client and Oracle Database communicate.
|
||||
|
||||
.. figure:: /images/cx_Oracle_arch.png
|
||||
|
||||
cx_Oracle Architecture
|
||||
|
||||
|
||||
Quick Start cx_Oracle Installation
|
||||
==================================
|
||||
|
||||
You can:
|
||||
|
||||
- Install `Python <https://www.python.org/downloads>`__ 3, if not already
|
||||
available. On macOS you must always install your own Python.
|
||||
|
||||
Python 3.6 and higher are supported by cx_Oracle 8.3. If you use Python 2,
|
||||
then the older cx_Oracle 7.3 will install.
|
||||
|
||||
- Install cx_Oracle from `PyPI
|
||||
<https://pypi.org/project/cx-Oracle/>`__ with:
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
python -m pip install cx_Oracle --upgrade
|
||||
|
||||
Note: if a binary wheel 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 don't have permission to write to
|
||||
system directories:
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
python -m pip install cx_Oracle --upgrade --user
|
||||
|
||||
If you are behind a proxy, add a proxy server to the command, for example add
|
||||
``--proxy=http://proxy.example.com:80``
|
||||
|
||||
- Add Oracle 21, 19, 18, 12 or 11.2 client libraries to your operating system
|
||||
library search path such as ``PATH`` on Windows or ``LD_LIBRARY_PATH`` on
|
||||
Linux. On macOS use :meth:`~cx_Oracle.init_oracle_client()` in your
|
||||
application to pass the Oracle Client directory name, see
|
||||
:ref:`usinginitoracleclient`. This is also usable on Windows.
|
||||
|
||||
To get the libraries:
|
||||
|
||||
- If your database is on a remote computer, then download and unzip the client
|
||||
libraries from the free `Oracle Instant Client
|
||||
<https://www.oracle.com/database/technologies/instant-client.html>`__
|
||||
"Basic" or "Basic Light" package for your operating system
|
||||
architecture.
|
||||
|
||||
Instant Client on Windows requires an appropriate Microsoft Windows
|
||||
Redistributables, see :ref:`wininstall`. On Linux, the ``libaio``
|
||||
(sometimes called ``libaio1``) package is needed. Oracle Linux 8 also
|
||||
needs the ``libnsl`` package.
|
||||
|
||||
- Alternatively, use the client libraries already available in a
|
||||
locally installed database such as the free `Oracle Database
|
||||
Express Edition ("XE")
|
||||
<https://www.oracle.com/database/technologies/appdev/xe.html>`__
|
||||
release.
|
||||
|
||||
Version 21 client libraries can connect to Oracle Database 12.1 or greater.
|
||||
Version 19, 18 and 12.2 client libraries can connect to Oracle Database 11.2
|
||||
or greater. Version 12.1 client libraries can connect to Oracle Database 10.2
|
||||
or greater. Version 11.2 client libraries can connect to Oracle Database 9.2
|
||||
or greater.
|
||||
|
||||
- Create a script like the one below:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# myscript.py
|
||||
|
||||
import cx_Oracle
|
||||
|
||||
# Connect as user "hr" with password "welcome" to the "orclpdb1" service running on this computer.
|
||||
connection = cx_Oracle.connect(user="hr", password="welcome",
|
||||
dsn="localhost/orclpdb1")
|
||||
|
||||
cursor = connection.cursor()
|
||||
cursor.execute("""
|
||||
SELECT first_name, last_name
|
||||
FROM employees
|
||||
WHERE department_id = :did AND employee_id > :eid""",
|
||||
did = 50,
|
||||
eid = 190)
|
||||
for fname, lname in cursor:
|
||||
print("Values:", fname, lname)
|
||||
|
||||
Locate your Oracle Database username and password, and the database
|
||||
connection string. The connection string is commonly of the format
|
||||
``hostname/servicename``, using the hostname where the database is
|
||||
running, and using the service name of the Oracle Database instance.
|
||||
|
||||
Substitute your username, password and connection string in the
|
||||
code. Run the Python script, for example::
|
||||
|
||||
python myscript.py
|
||||
|
||||
You can learn how to use cx_Oracle from the :ref:`API documentation <module>`
|
||||
and `samples
|
||||
<https://github.com/oracle/python-cx_Oracle/blob/main/samples>`__.
|
||||
|
||||
If you run into installation trouble, check out the section on `Troubleshooting`_.
|
||||
|
||||
|
||||
Oracle Client and Oracle Database Interoperability
|
||||
==================================================
|
||||
|
||||
cx_Oracle requires Oracle Client libraries. The libraries provide the
|
||||
necessary network connectivity to access an Oracle Database instance.
|
||||
They also provide basic and advanced connection management and data
|
||||
features to cx_Oracle.
|
||||
|
||||
The simplest way to get Oracle Client libraries is to install the free
|
||||
`Oracle Instant Client
|
||||
<https://www.oracle.com/database/technologies/instant-client.html>`__
|
||||
"Basic" or "Basic Light" package. The libraries are also available in
|
||||
any Oracle Database installation or full Oracle Client installation.
|
||||
|
||||
Oracle's standard client-server network interoperability allows
|
||||
connections between different versions of Oracle Client libraries and
|
||||
Oracle Database. For certified configurations see Oracle Support's
|
||||
`Doc ID 207303.1
|
||||
<https://support.oracle.com/epmos/faces/DocumentDisplay?id=207303.1>`__.
|
||||
In summary, Oracle Client 21 can connect to Oracle Database 12.1 or greater.
|
||||
Oracle Client 19, 18 and 12.2 can connect to Oracle Database 11.2 or
|
||||
greater. Oracle Client 12.1 can connect to Oracle Database 10.2 or
|
||||
greater. Oracle Client 11.2 can connect to Oracle Database 9.2 or greater. The
|
||||
technical restrictions on creating connections may be more flexible. For
|
||||
example Oracle Client 12.2 can successfully connect to Oracle Database 10.2.
|
||||
|
||||
cx_Oracle uses the shared library loading mechanism available on each
|
||||
supported platform to load the Oracle Client libraries at runtime. It
|
||||
does not need to be rebuilt for different versions of the libraries.
|
||||
Since a single cx_Oracle binary can use different client versions and
|
||||
also access multiple database versions, it is important your
|
||||
application is tested in your intended release environments. Newer
|
||||
Oracle clients support new features, such as the `oraaccess.xml
|
||||
<https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-9D12F489-EC02-46BE-8CD4-5AECED0E2BA2>`__ external configuration
|
||||
file available with 12.1 or later clients, session pool improvements,
|
||||
improved high availability features, call timeouts, and `other enhancements
|
||||
<https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-D60519C3-406F-4588-8DA1-D475D5A3E1F6>`__.
|
||||
|
||||
The cx_Oracle function :func:`~cx_Oracle.clientversion()` can be used to
|
||||
determine which Oracle Client version is in use. The attribute
|
||||
:attr:`Connection.version` can be used to determine which Oracle Database
|
||||
version a connection is accessing. These can then be used to adjust application
|
||||
behavior accordingly. Attempts to use Oracle features that are not supported by
|
||||
a particular client/server library combination will result in runtime errors.
|
||||
|
||||
Installing cx_Oracle on Linux
|
||||
=============================
|
||||
|
||||
This section discusses the generic installation methods on Linux. To use Python
|
||||
and cx_Oracle RPM packages from yum on Oracle Linux, see :ref:`oraclelinux`.
|
||||
|
||||
.. note::
|
||||
|
||||
**cx_Oracle has a major new release under a new name and homepage**
|
||||
`python-oracledb <https://oracle.github.io/python-oracledb/>`__.
|
||||
|
||||
**New projects should install python-oracledb instead of cx_Oracle.**
|
||||
|
||||
Install cx_Oracle
|
||||
-----------------
|
||||
|
||||
The generic way to install cx_Oracle on Linux is to use Python's `Pip
|
||||
<https://pip.readthedocs.io/en/latest/installing/>`__ package to
|
||||
install cx_Oracle from `PyPI
|
||||
<https://pypi.org/project/cx-Oracle/>`__:
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
python -m pip install cx_Oracle --upgrade
|
||||
|
||||
The ``--user`` option may be useful, if you don't have permission to write to
|
||||
system directories:
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
python -m pip install cx_Oracle --upgrade --user
|
||||
|
||||
If you are behind a proxy, add a proxy server to the command, for example add
|
||||
``--proxy=http://proxy.example.com:80``
|
||||
|
||||
This will download and install a pre-compiled binary `if one is
|
||||
available <https://pypi.org/project/cx-Oracle/>`__ for your
|
||||
architecture. If a pre-compiled binary is not available, the source
|
||||
will be downloaded, compiled, and the resulting binary installed.
|
||||
Compiling cx_Oracle requires the ``Python.h`` header file. If you are
|
||||
using the default ``python`` package, this file is in the ``python-devel``
|
||||
package or equivalent.
|
||||
|
||||
Install Oracle Client
|
||||
---------------------
|
||||
|
||||
Using cx_Oracle requires Oracle Client libraries to be installed.
|
||||
These provide the necessary network connectivity allowing cx_Oracle
|
||||
to access an Oracle Database instance.
|
||||
|
||||
- If your database is on a remote computer, then download the free `Oracle
|
||||
Instant Client
|
||||
<https://www.oracle.com/database/technologies/instant-client.html>`__
|
||||
"Basic" or "Basic Light" package for your operating system
|
||||
architecture. Use the RPM or ZIP packages, based on your
|
||||
preferences.
|
||||
|
||||
- Alternatively, use the client libraries already available in a
|
||||
locally installed database such as the free `Oracle Database
|
||||
Express Edition ("XE")
|
||||
<https://www.oracle.com/database/technologies/appdev/xe.html>`__
|
||||
release.
|
||||
|
||||
Oracle Instant Client Zip Files
|
||||
+++++++++++++++++++++++++++++++
|
||||
|
||||
To use cx_Oracle with Oracle Instant Client zip files:
|
||||
|
||||
1. Download an Oracle 21, 19, 18, 12, or 11.2 "Basic" or "Basic Light" zip file
|
||||
matching your Python 64-bit or 32-bit architecture:
|
||||
|
||||
- `x86-64 64-bit <https://www.oracle.com/database/technologies/instant-client/linux-x86-64-downloads.html>`__
|
||||
- `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>`__
|
||||
|
||||
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:
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
mkdir -p /opt/oracle
|
||||
cd /opt/oracle
|
||||
unzip instantclient-basic-linux.x64-21.1.0.0.0.zip
|
||||
|
||||
3. Install the ``libaio`` package with sudo or as the root user. For example::
|
||||
|
||||
sudo yum install libaio
|
||||
|
||||
On some Linux distributions this package is called ``libaio1`` instead.
|
||||
|
||||
On recent Linux versions such as Oracle Linux 8, you may also need to
|
||||
install the ``libnsl`` package when using Oracle Instant Client 19.
|
||||
|
||||
4. If there is no other Oracle software on the machine that will be
|
||||
impacted, permanently add Instant Client to the runtime link
|
||||
path. For example, with sudo or as the root user:
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
sudo sh -c "echo /opt/oracle/instantclient_21_1 > /etc/ld.so.conf.d/oracle-instantclient.conf"
|
||||
sudo ldconfig
|
||||
|
||||
Alternatively, set the environment variable ``LD_LIBRARY_PATH`` to
|
||||
the appropriate directory for the Instant Client version. For
|
||||
example::
|
||||
|
||||
export LD_LIBRARY_PATH=/opt/oracle/instantclient_21_1:$LD_LIBRARY_PATH
|
||||
|
||||
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
|
||||
``/opt/oracle/your_config_dir``. Then use:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import cx_Oracle
|
||||
cx_Oracle.init_oracle_client(config_dir="/home/your_username/oracle/your_config_dir")
|
||||
|
||||
Or set the environment variable ``TNS_ADMIN`` to that directory name.
|
||||
|
||||
Alternatively, put the files in the ``network/admin`` subdirectory of Instant
|
||||
Client, for example in ``/opt/oracle/instantclient_21_1/network/admin``.
|
||||
This is the default Oracle configuration directory for executables linked
|
||||
with this Instant Client.
|
||||
|
||||
Oracle Instant Client RPMs
|
||||
++++++++++++++++++++++++++
|
||||
|
||||
To use cx_Oracle with Oracle Instant Client RPMs:
|
||||
|
||||
1. Download an Oracle 21,19, 18, 12, or 11.2 "Basic" or "Basic Light" RPM
|
||||
matching your Python architecture:
|
||||
|
||||
- `x86-64 64-bit <https://www.oracle.com/database/technologies/instant-client/linux-x86-64-downloads.html>`__
|
||||
- `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's yum server has convenient repositories:
|
||||
|
||||
- `Instant Client 21 RPMs for Oracle Linux x86-64 8 <https://yum.oracle.com/repo/OracleLinux/OL8/oracle/instantclient21/x86_64/index.html>`__, `Older Instant Client RPMs for Oracle Linux x86-64 8 <https://yum.oracle.com/repo/OracleLinux/OL8/oracle/instantclient/x86_64/index.html>`__
|
||||
- `Instant Client 21 RPMs for Oracle Linux x86-64 7 <https://yum.oracle.com/repo/OracleLinux/OL7/oracle/instantclient21/x86_64/index.html>`__, `Older Instant Client RPMs for Oracle Linux x86-64 7 <https://yum.oracle.com/repo/OracleLinux/OL7/oracle/instantclient/x86_64/index.html>`__
|
||||
- `Instant Client RPMs for Oracle Linux x86-64 6 <https://yum.oracle.com/repo/OracleLinux/OL6/oracle/instantclient/x86_64/index.html>`__
|
||||
- `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>`__
|
||||
|
||||
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:
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
sudo yum install oracle-instantclient-basic-21.1.0.0.0-1.x86_64.rpm
|
||||
|
||||
Yum will automatically install required dependencies, such as ``libaio``.
|
||||
|
||||
On recent Linux versions, such as Oracle Linux 8, you may need to manually
|
||||
install the ``libnsl`` package when using Oracle Instant Client 19.
|
||||
|
||||
3. For Instant Client 19, or later, the system library search path is
|
||||
automatically configured during installation.
|
||||
|
||||
For older versions, if there is no other Oracle software on the machine that will be
|
||||
impacted, permanently add Instant Client to the runtime link
|
||||
path. For example, with sudo or as the root user:
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
sudo sh -c "echo /usr/lib/oracle/18.5/client64/lib > /etc/ld.so.conf.d/oracle-instantclient.conf"
|
||||
sudo ldconfig
|
||||
|
||||
Alternatively, for version 18 and earlier, every shell running
|
||||
Python will need to have the environment variable
|
||||
``LD_LIBRARY_PATH`` set to the appropriate directory for the
|
||||
Instant Client version. For example::
|
||||
|
||||
export LD_LIBRARY_PATH=/usr/lib/oracle/18.5/client64/lib:$LD_LIBRARY_PATH
|
||||
|
||||
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
|
||||
``/opt/oracle/your_config_dir``. Then use:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import cx_Oracle
|
||||
cx_Oracle.init_oracle_client(config_dir="/opt/oracle/your_config_dir")
|
||||
|
||||
Or set the environment variable ``TNS_ADMIN`` to that directory name.
|
||||
|
||||
Alternatively, put the files in the ``network/admin`` subdirectory of Instant
|
||||
Client, for example in ``/usr/lib/oracle/21/client64/lib/network/admin``.
|
||||
This is the default Oracle configuration directory for executables linked
|
||||
with this Instant Client.
|
||||
|
||||
Local Database or Full Oracle Client
|
||||
++++++++++++++++++++++++++++++++++++
|
||||
|
||||
cx_Oracle applications can use Oracle Client 21, 19, 18, 12, or 11.2 libraries
|
||||
from a local Oracle Database or full Oracle Client installation.
|
||||
|
||||
The libraries must be either 32-bit or 64-bit, matching your
|
||||
Python architecture.
|
||||
|
||||
1. Set required Oracle environment variables by running the Oracle environment
|
||||
script. For example:
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
source /usr/local/bin/oraenv
|
||||
|
||||
For Oracle Database Express Edition ("XE") 11.2, run:
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
source /u01/app/oracle/product/11.2.0/xe/bin/oracle_env.sh
|
||||
|
||||
2. Optional Oracle configuration files such as ``tnsnames.ora``,
|
||||
``sqlnet.ora`` or ``oraaccess.xml`` can be placed in
|
||||
``$ORACLE_HOME/network/admin``.
|
||||
|
||||
Alternatively, Oracle configuration files can be put in another,
|
||||
accessible directory. Then set the environment variable
|
||||
``TNS_ADMIN`` to that directory name.
|
||||
|
||||
|
||||
.. _oraclelinux:
|
||||
|
||||
Installing cx_Oracle RPMs on Oracle Linux
|
||||
=========================================
|
||||
|
||||
Python and cx_Oracle RPM packages are available from the `Oracle Linux yum server
|
||||
<https://yum.oracle.com/>`__. Various versions of Python are easily installed.
|
||||
Using the yum server makes it easy to keep up to date.
|
||||
|
||||
Installation instructions are at `Oracle Linux for Python
|
||||
Developers <https://yum.oracle.com/oracle-linux-python.html>`__.
|
||||
|
||||
.. _wininstall:
|
||||
|
||||
Installing cx_Oracle on Windows
|
||||
===============================
|
||||
|
||||
.. note::
|
||||
|
||||
**cx_Oracle has a major new release under a new name and homepage**
|
||||
`python-oracledb <https://oracle.github.io/python-oracledb/>`__.
|
||||
|
||||
**New projects should install python-oracledb instead of cx_Oracle.**
|
||||
|
||||
Install cx_Oracle
|
||||
-----------------
|
||||
|
||||
Use Python's `Pip <https://pip.readthedocs.io/en/latest/installing/>`__
|
||||
package to install cx_Oracle from `PyPI
|
||||
<https://pypi.org/project/cx-Oracle/>`__::
|
||||
|
||||
python -m pip install cx_Oracle --upgrade
|
||||
|
||||
If you are behind a proxy, specify your proxy server:
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
python -m pip install cx_Oracle --proxy=http://proxy.example.com:80 --upgrade
|
||||
|
||||
This will download and install a pre-compiled binary `if one is
|
||||
available <https://pypi.org/project/cx-Oracle/>`__ for your
|
||||
architecture. If a pre-compiled binary is not available, the source
|
||||
will be downloaded, compiled, and the resulting binary installed.
|
||||
|
||||
Install Oracle Client
|
||||
---------------------
|
||||
|
||||
Using cx_Oracle requires Oracle Client libraries to be installed.
|
||||
These provide the necessary network connectivity allowing cx_Oracle
|
||||
to access an Oracle Database instance. Oracle Client versions 19, 18,
|
||||
12 and 11.2 are supported.
|
||||
|
||||
- If your database is on a remote computer, then download the free `Oracle
|
||||
Instant Client
|
||||
<https://www.oracle.com/database/technologies/instant-client.html>`__
|
||||
"Basic" 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
|
||||
Express Edition ("XE")
|
||||
<https://www.oracle.com/database/technologies/appdev/xe.html>`__
|
||||
release.
|
||||
|
||||
|
||||
Oracle Instant Client Zip Files
|
||||
+++++++++++++++++++++++++++++++
|
||||
|
||||
To use cx_Oracle with Oracle Instant Client zip files:
|
||||
|
||||
1. Download an Oracle 19, 18, 12, or 11.2 "Basic" or "Basic Light" zip
|
||||
file: `64-bit
|
||||
<https://www.oracle.com/database/technologies/instant-client/winx64-64-downloads.html>`__
|
||||
or `32-bit
|
||||
<https://www.oracle.com/database/technologies/instant-client/microsoft-windows-32-downloads.html>`__, matching your
|
||||
Python architecture.
|
||||
|
||||
The latest version is recommended. Oracle Instant Client 19 will
|
||||
connect to Oracle Database 11.2 or later.
|
||||
|
||||
Windows 7 users: Note that Oracle 19c is not supported on Windows 7.
|
||||
|
||||
2. Unzip the package into a directory that is accessible to your
|
||||
application. For example unzip
|
||||
``instantclient-basic-windows.x64-19.11.0.0.0dbru.zip`` to
|
||||
``C:\oracle\instantclient_19_11``.
|
||||
|
||||
3. Oracle Instant Client libraries require a Visual Studio redistributable with
|
||||
a 64-bit or 32-bit architecture to match Instant Client's architecture.
|
||||
Each Instant Client version requires a different redistributable version:
|
||||
|
||||
- For Instant Client 21 install `VS 2019 <https://docs.microsoft.com/en-US/cpp/windows/latest-supported-vc-redist?view=msvc-170>`__ or later.
|
||||
- For Instant Client 19 install `VS 2017 <https://docs.microsoft.com/en-US/cpp/windows/latest-supported-vc-redist?view=msvc-170>`__.
|
||||
- For Instant Client 18 or 12.2 install `VS 2013 <https://docs.microsoft.com/en-US/cpp/windows/latest-supported-vc-redist?view=msvc-170#visual-studio-2013-vc-120>`__
|
||||
- For Instant Client 12.1 install `VS 2010 <https://docs.microsoft.com/en-US/cpp/windows/latest-supported-vc-redist?view=msvc-170#visual-studio-2010-vc-100-sp1-no-longer-supported>`__
|
||||
- For Instant Client 11.2 install `VS 2005 64-bit <https://docs.microsoft.com/en-US/cpp/windows/latest-supported-vc-redist?view=msvc-170#visual-studio-2005-vc-80-sp1-no-longer-supported>`__
|
||||
|
||||
Configure Oracle Instant Client
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
1. There are several alternative ways to tell cx_Oracle where your Oracle Client
|
||||
libraries are, see :ref:`initialization`.
|
||||
|
||||
* With Oracle Instant Client you can use :meth:`~cx_Oracle.init_oracle_client()`
|
||||
in your application, for example:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import cx_Oracle
|
||||
cx_Oracle.init_oracle_client(lib_dir=r"C:\oracle\instantclient_19_11")
|
||||
|
||||
Note a 'raw' string is used because backslashes occur in the path.
|
||||
|
||||
* Alternatively, add the Oracle Instant Client directory to the ``PATH``
|
||||
environment variable. The directory must occur in ``PATH`` before any
|
||||
other Oracle directories. Restart any open command prompt windows.
|
||||
|
||||
* Another way to set ``PATH`` is to use a batch file that sets it before Python
|
||||
is executed, for example::
|
||||
|
||||
REM mypy.bat
|
||||
SET PATH=C:\oracle\instantclient_19_9;%PATH%
|
||||
python %*
|
||||
|
||||
Invoke this batch file every time you want to run Python.
|
||||
|
||||
2. 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
|
||||
``C:\oracle\your_config_dir``. Then use:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import cx_Oracle
|
||||
cx_Oracle.init_oracle_client(lib_dir=r"C:\oracle\instantclient_19_11",
|
||||
config_dir=r"C:\oracle\your_config_dir")
|
||||
|
||||
Or set the environment variable ``TNS_ADMIN`` to that directory name.
|
||||
|
||||
Alternatively, put the files in a ``network\admin`` subdirectory of
|
||||
Instant Client, for example in
|
||||
``C:\oracle\instantclient_19_11\network\admin``. This is the default
|
||||
Oracle configuration directory for executables linked with this
|
||||
Instant Client.
|
||||
|
||||
|
||||
Local Database or Full Oracle Client
|
||||
++++++++++++++++++++++++++++++++++++
|
||||
|
||||
cx_Oracle applications can use Oracle Client 19, 18, 12, or 11.2
|
||||
libraries libraries from a local Oracle Database or full Oracle
|
||||
Client.
|
||||
|
||||
The Oracle libraries must be either 32-bit or 64-bit, matching your
|
||||
Python architecture.
|
||||
|
||||
1. Set the environment variable ``PATH`` to include the path that contains
|
||||
``OCI.DLL``, if it is not already set.
|
||||
|
||||
Restart any open command prompt windows.
|
||||
|
||||
2. Optional Oracle configuration files such as ``tnsnames.ora``,
|
||||
``sqlnet.ora`` or ``oraaccess.xml`` can be placed in the
|
||||
``network\admin`` subdirectory of the Oracle Database software
|
||||
installation.
|
||||
|
||||
Alternatively, pass ``config_dir`` to :meth:`~cx_Oracle.init_oracle_client()`
|
||||
as shown in the previous section, or set ``TNS_ADMIN`` to the directory name.
|
||||
|
||||
Installing cx_Oracle on macOS (Intel x86)
|
||||
=========================================
|
||||
|
||||
.. note::
|
||||
|
||||
**cx_Oracle has a major new release under a new name and homepage**
|
||||
`python-oracledb <https://oracle.github.io/python-oracledb/>`__.
|
||||
|
||||
**New projects should install python-oracledb instead of cx_Oracle.**
|
||||
|
||||
Install Python
|
||||
--------------
|
||||
|
||||
Make sure you are not using the bundled Python. This has restricted
|
||||
entitlements and will fail to load Oracle client libraries. Instead use
|
||||
`Homebrew <https://brew.sh>`__ or `Python.org
|
||||
<https://www.python.org/downloads>`__.
|
||||
|
||||
A C compiler is needed, for example Xcode and its command line tools.
|
||||
|
||||
Install cx_Oracle
|
||||
-----------------
|
||||
|
||||
Use Python's `Pip <https://pip.readthedocs.io/en/latest/installing/>`__
|
||||
package to install cx_Oracle from `PyPI
|
||||
<https://pypi.org/project/cx-Oracle/>`__:
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
export ARCHFLAGS="-arch x86_64"
|
||||
python -m pip install cx_Oracle --upgrade
|
||||
|
||||
The ``--user`` option may be useful, if you don't have permission to write to
|
||||
system directories:
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
python -m pip install cx_Oracle --upgrade --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.
|
||||
|
||||
Install Oracle Instant Client
|
||||
-----------------------------
|
||||
|
||||
Oracle Instant Client provides the network connectivity for accessing Oracle
|
||||
Database.
|
||||
|
||||
Manual Installation
|
||||
+++++++++++++++++++
|
||||
|
||||
* Download the **Basic** 64-bit DMG from `Oracle
|
||||
<https://www.oracle.com/database/technologies/instant-client/macos-intel-x86-downloads.html>`__.
|
||||
|
||||
* In Finder, double click on the DMG to mount it.
|
||||
|
||||
* Open a terminal window and run the install script in the mounted package, for example:
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
/Volumes/instantclient-basic-macos.x64-19.8.0.0.0dbru/install_ic.sh
|
||||
|
||||
This copies the contents to ``$HOME/Downloads/instantclient_19_8``.
|
||||
Applications may not have access to the ``Downloads`` directory, so you
|
||||
should move Instant Client somewhere convenient.
|
||||
|
||||
* In Finder, eject the mounted Instant Client package.
|
||||
|
||||
If you have multiple Instant Client DMG packages mounted, you only need to run
|
||||
``install_ic.sh`` once. It will copy all mounted Instant Client DMG packages at
|
||||
the same time.
|
||||
|
||||
Scripted Installation
|
||||
+++++++++++++++++++++
|
||||
|
||||
Instant Client installation can alternatively be scripted, for example:
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
cd $HOME/Downloads
|
||||
curl -O https://download.oracle.com/otn_software/mac/instantclient/198000/instantclient-basic-macos.x64-19.8.0.0.0dbru.dmg
|
||||
hdiutil mount instantclient-basic-macos.x64-19.8.0.0.0dbru.dmg
|
||||
/Volumes/instantclient-basic-macos.x64-19.8.0.0.0dbru/install_ic.sh
|
||||
hdiutil unmount /Volumes/instantclient-basic-macos.x64-19.8.0.0.0dbru
|
||||
|
||||
The Instant Client directory will be ``$HOME/Downloads/instantclient_19_8``.
|
||||
Applications may not have access to the ``Downloads`` directory, so you should
|
||||
move Instant Client somewhere convenient.
|
||||
|
||||
|
||||
Configure Oracle Instant Client
|
||||
-------------------------------
|
||||
|
||||
1. Call :meth:`~cx_Oracle.init_oracle_client()` once in your application:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import cx_Oracle
|
||||
cx_Oracle.init_oracle_client(lib_dir="/Users/your_username/Downloads/instantclient_19_8")
|
||||
|
||||
2. If you use optional Oracle configuration files such as ``tnsnames.ora``,
|
||||
``sqlnet.ora`` or ``oraaccess.xml`` with Oracle Instant Client, then put the
|
||||
files in an accessible directory, for example in
|
||||
``/Users/your_username/oracle/your_config_dir``. Then use:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import cx_Oracle
|
||||
cx_Oracle.init_oracle_client(lib_dir="/Users/your_username/Downloads/instantclient_19_8",
|
||||
config_dir="/Users/your_username/oracle/your_config_dir")
|
||||
|
||||
Or set the environment variable ``TNS_ADMIN`` to that directory name.
|
||||
|
||||
Alternatively, put the files in the ``network/admin`` subdirectory of Oracle
|
||||
Instant Client, for example in
|
||||
``/Users/your_username/Downloads/instantclient_19_8/network/admin``. This is the
|
||||
default Oracle configuration directory for executables linked with this
|
||||
Instant Client.
|
||||
|
||||
Linux Containers
|
||||
================
|
||||
|
||||
Sample Dockerfiles are on `GitHub
|
||||
<https://github.com/oracle/docker-images/tree/main/OracleLinuxDevelopers>`__.
|
||||
|
||||
Pre-built images for Python and cx_Oracle are in the `GitHub Container Registry
|
||||
<https://github.com/orgs/oracle/packages>`__. These are easily used. For
|
||||
example, to pull an Oracle Linux 8 image with Python 3.6 and cx_Oracle,
|
||||
execute::
|
||||
|
||||
docker pull ghcr.io/oracle/oraclelinux7-python:3.6-oracledb
|
||||
|
||||
|
||||
Installing cx_Oracle without Internet Access
|
||||
============================================
|
||||
|
||||
To install cx_Oracle on a computer that is not connected to the
|
||||
internet, download the appropriate cx_Oracle file from `PyPI
|
||||
<https://pypi.org/project/cx-Oracle/#files>`__. Transfer this file to
|
||||
the offline computer and install it with::
|
||||
|
||||
python -m pip install "<file_name>"
|
||||
|
||||
Then follow the general cx_Oracle platform installation instructions
|
||||
to install Oracle client libraries.
|
||||
|
||||
Install Using GitHub
|
||||
====================
|
||||
|
||||
In order to install using the source on GitHub, use the following commands::
|
||||
|
||||
git clone https://github.com/oracle/python-cx_Oracle.git cx_Oracle
|
||||
cd cx_Oracle
|
||||
git submodule init
|
||||
git submodule update
|
||||
python setup.py install
|
||||
|
||||
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 extract it
|
||||
inside the directory called "odpi".
|
||||
|
||||
cx_Oracle source code is also available from opensource.oracle.com. This can
|
||||
be cloned with::
|
||||
|
||||
git clone git://opensource.oracle.com/git/oracle/python-cx_Oracle.git cx_Oracle
|
||||
cd cx_Oracle
|
||||
git submodule init
|
||||
git submodule update
|
||||
|
||||
|
||||
Install Using Source from PyPI
|
||||
==============================
|
||||
|
||||
The source package can be downloaded manually from
|
||||
`PyPI <https://pypi.org/project/cx-Oracle/>`__ and extracted, after
|
||||
which the following commands should be run::
|
||||
|
||||
python setup.py build
|
||||
python setup.py install
|
||||
|
||||
|
||||
Upgrading from Older Versions
|
||||
=============================
|
||||
|
||||
Review the :ref:`release notes <releasenotes>` and :ref:`Deprecations
|
||||
<deprecations>` for changes. Modify affected code.
|
||||
|
||||
If you are upgrading from cx_Oracle 7 note these changes:
|
||||
|
||||
- The default character set used by cx_Oracle 8 is now "UTF-8". Also, the
|
||||
character set component of the ``NLS_LANG`` environment variable is
|
||||
ignored. If you need to change the character set, then pass ``encoding``
|
||||
and ``nendcoding`` parameters when creating a connection or connection
|
||||
pool. See :ref:`globalization`.
|
||||
|
||||
- Any uses of ``type(var)`` need to be changed to ``var.type``.
|
||||
|
||||
- Any uses of ``var.type is not None`` need to be changed to
|
||||
``isinstance(var.type, cx_Oracle.ObjectType)``
|
||||
|
||||
- Note that ``TIMESTAMP WITH TIME ZONE`` columns will now be reported as
|
||||
:data:`cx_Oracle.DB_TYPE_TIMESTAMP_TZ` instead of
|
||||
:data:`cx_Oracle.TIMESTAMP` in :data:`Cursor.description`.
|
||||
|
||||
- Note that ``TIMESTAMP WITH LOCAL TIME ZONE`` columns will now be reported
|
||||
as :data:`cx_Oracle.DB_TYPE_TIMESTAMP_LTZ` instead of
|
||||
:data:`cx_Oracle.TIMESTAMP` in :data:`Cursor.description`.
|
||||
|
||||
- Note that ``BINARY_FLOAT`` columns will now be reported as
|
||||
:data:`cx_Oracle.DB_TYPE_BINARY_FLOAT` instead of
|
||||
:data:`cx_Oracle.NATIVE_DOUBLE` in :data:`Cursor.description`.
|
||||
|
||||
If you are upgrading from cx_Oracle 5 note these installation changes:
|
||||
|
||||
- When using Oracle Instant Client, you should not set ``ORACLE_HOME``.
|
||||
|
||||
- On Linux, cx_Oracle 6 and higher no longer uses Instant Client RPMs
|
||||
automatically. You must set ``LD_LIBRARY_PATH`` or use ``ldconfig`` to
|
||||
locate the Oracle client library.
|
||||
|
||||
- PyPI no longer allows Windows installers or Linux RPMs to be
|
||||
hosted. Use the supplied cx_Oracle Wheels instead, or use RPMs
|
||||
from Oracle, see :ref:`oraclelinux`.
|
||||
|
||||
.. _python2:
|
||||
|
||||
Installing cx_Oracle in Python 2
|
||||
================================
|
||||
|
||||
cx_Oracle 7.3 was the last version with support for Python 2.
|
||||
|
||||
If you install cx_Oracle in Python 2 using the commands provided above, then
|
||||
cx_Oracle 7.3 will be installed. This is equivalent to using a command like::
|
||||
|
||||
python -m pip install cx_Oracle==7.3 --upgrade --user
|
||||
|
||||
For other installation options such as installing through a proxy, see
|
||||
instructions above. Make sure the Oracle Client libraries are in the system
|
||||
library search path because cx_Oracle 7 does not support the
|
||||
:meth:`cx_Oracle.init_oracle_client()` method and does not support loading the
|
||||
Oracle Client libraries from the directory containing the cx_Oracle module
|
||||
binary.
|
||||
|
||||
Installing cx_Oracle 5.3
|
||||
========================
|
||||
|
||||
If you require cx_Oracle 5.3, download a Windows installer from `PyPI
|
||||
<https://pypi.org/project/cx-Oracle/>`__ or use ``python -m pip
|
||||
install cx-oracle==5.3`` to install from source.
|
||||
|
||||
Very old versions of cx_Oracle can be found in the files section at
|
||||
`SourceForce <https://sourceforge.net/projects/cx-oracle/files/>`__.
|
||||
|
||||
|
||||
Troubleshooting
|
||||
===============
|
||||
|
||||
If installation fails:
|
||||
|
||||
- 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.
|
||||
|
||||
- Was there a network connection error? Do you need to set the
|
||||
environment variables ``http_proxy`` and/or ``https_proxy``? Or
|
||||
try ``pip install --proxy=http://proxy.example.com:80 cx_Oracle
|
||||
--upgrade``?
|
||||
|
||||
- If upgrading gave no errors but the old version is still
|
||||
installed, try ``pip install cx_Oracle --upgrade
|
||||
--force-reinstall``
|
||||
|
||||
- If you do not have access to modify your system version of
|
||||
Python, can you use ``pip install cx_Oracle --upgrade --user``
|
||||
or venv?
|
||||
|
||||
- Do you get the error "``No module named pip``"? The pip module is builtin
|
||||
to Python but is sometimes removed by the OS. Use the venv module
|
||||
(builtin to Python 3.x) or virtualenv module instead.
|
||||
|
||||
- Do you get the error "``fatal error: dpi.h: No such file or directory``"
|
||||
when building from source code? Ensure that your source installation has
|
||||
a subdirectory called "odpi" containing files. If missing, review the
|
||||
section on `Install Using GitHub`_.
|
||||
|
||||
If using cx_Oracle fails:
|
||||
|
||||
- Do you get the error "``DPI-1047: Oracle Client library cannot be
|
||||
loaded``"?
|
||||
|
||||
- On Windows and macOS, try using :meth:`~cx_Oracle.init_oracle_client()`.
|
||||
See :ref:`usinginitoracleclient`.
|
||||
|
||||
- Check that 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
|
||||
cx_Oracle. The trace messages will show how and where cx_Oracle 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 used :meth:`~cx_Oracle.init_oracle_client()` and have
|
||||
a full database installation, make sure 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
|
||||
:meth:`~cx_Oracle.init_oracle_client()`, then restart your command prompt
|
||||
and use ``set PATH`` to check 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 ``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 the ``LD_LIBRARY_PATH`` environment variable contains
|
||||
the Oracle Client library directory. If you are using Oracle Instant
|
||||
Client, a preferred alternative is to ensure a file in the
|
||||
``/etc/ld.so.conf.d`` directory contains the path to the Instant Client
|
||||
directory, and then run ``ldconfig``.
|
||||
|
||||
- On macOS, make sure you are not using the bundled Python (use `Homebrew
|
||||
<https://brew.sh>`__ or `Python.org
|
||||
<https://www.python.org/downloads>`__ instead). If you are not using
|
||||
:meth:`~cx_Oracle.init_oracle_client()`, then put the Oracle Instant
|
||||
Client libraries in ``~/lib`` or ``/usr/local/lib``.
|
||||
|
||||
- If you got "``DPI-1072: the Oracle Client library version is
|
||||
unsupported``", then review the installation requirements. cx_Oracle
|
||||
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.
|
||||
|
||||
- If you have multiple versions of Python installed, make sure you are
|
||||
using the correct python and pip (or python3 and pip3) executables.
|
|
@ -1,145 +0,0 @@
|
|||
.. _introduction:
|
||||
|
||||
*************************
|
||||
Introduction to cx_Oracle
|
||||
*************************
|
||||
|
||||
cx_Oracle is a Python extension module that enables Python access to 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.
|
||||
|
||||
.. note::
|
||||
|
||||
**cx_Oracle has a major new release under a new name and homepage**
|
||||
`python-oracledb <https://oracle.github.io/python-oracledb/>`__.
|
||||
|
||||
**New projects should install python-oracledb instead of cx_Oracle.**
|
||||
|
||||
Architecture
|
||||
------------
|
||||
|
||||
Python programs call cx_Oracle functions. Internally cx_Oracle dynamically
|
||||
loads Oracle Client libraries to access Oracle Database. The database can be on
|
||||
the same machine as Python, or it can be remote.
|
||||
|
||||
.. _archfig:
|
||||
.. figure:: /images/cx_Oracle_arch.png
|
||||
|
||||
cx_Oracle Architecture
|
||||
|
||||
cx_Oracle is typically installed from `PyPI
|
||||
<https://pypi.org/project/cx-Oracle/>`__ using `pip
|
||||
<https://pip.pypa.io/en/latest/installing/>`__. The Oracle Client libraries
|
||||
need to be installed separately. The libraries can be from an installation of
|
||||
`Oracle Instant Client
|
||||
<https://www.oracle.com/database/technologies/instant-client.html>`__, from a
|
||||
full Oracle Client installation, or even from an Oracle Database installation
|
||||
(if Python is running on the same machine as the database). Oracle’s standard
|
||||
client-server version interoperability allows connection to both older and
|
||||
newer databases from different Client library versions, see :ref:`cx_Oracle
|
||||
Installation <installation>`.
|
||||
|
||||
Some behaviors of the Oracle Client libraries can optionally be configured with
|
||||
an ``oraaccess.xml`` file, for example to enable auto-tuning of a statement
|
||||
cache. See :ref:`optclientfiles`.
|
||||
|
||||
The Oracle Net layer can optionally be configured with files such as
|
||||
``tnsnames.ora`` and ``sqlnet.ora``, for example to enable :ref:`network
|
||||
encryption <netencrypt>`. See :ref:`optnetfiles`.
|
||||
|
||||
Oracle environment variables that are set before cx_Oracle first creates a
|
||||
database connection will affect cx_Oracle behavior. Optional variables include
|
||||
NLS_LANG, NLS_DATE_FORMAT and TNS_ADMIN. See :ref:`envset`.
|
||||
|
||||
Features
|
||||
--------
|
||||
|
||||
The cx_Oracle feature highlights are:
|
||||
|
||||
* Easy installation from PyPI
|
||||
* Support for multiple Oracle Client and Database versions
|
||||
* Execution of SQL and PL/SQL statements
|
||||
* Extensive Oracle data type support, including large objects (CLOB and
|
||||
BLOB) and binding of SQL objects
|
||||
* Connection management, including connection pooling
|
||||
* Oracle Database High Availability features
|
||||
* Full use of Oracle Network Service infrastructure, including encrypted
|
||||
network traffic and security features
|
||||
|
||||
A complete list of supported features can be seen `here
|
||||
<https://oracle.github.io/python-cx_Oracle/index.html#features>`_.
|
||||
|
||||
Getting Started
|
||||
---------------
|
||||
|
||||
Install cx_Oracle using the :ref:`installation <installation>` steps.
|
||||
|
||||
Create a script ``query.py`` as shown below:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# query.py
|
||||
|
||||
import cx_Oracle
|
||||
|
||||
# Establish the database connection
|
||||
connection = cx_Oracle.connect(user="hr", password=userpwd,
|
||||
dsn="dbhost.example.com/orclpdb1")
|
||||
|
||||
# Obtain a cursor
|
||||
cursor = connection.cursor()
|
||||
|
||||
# Data for binding
|
||||
manager_id = 145
|
||||
first_name = "Peter"
|
||||
|
||||
# Execute the query
|
||||
sql = """SELECT first_name, last_name
|
||||
FROM employees
|
||||
WHERE manager_id = :mid AND first_name = :fn"""
|
||||
cursor.execute(sql, mid=manager_id, fn=first_name)
|
||||
|
||||
# Loop over the result set
|
||||
for row in cursor:
|
||||
print(row)
|
||||
|
||||
This uses Oracle's `sample HR schema
|
||||
<https://github.com/oracle/db-sample-schemas>`__.
|
||||
|
||||
Simple :ref:`connection <connhandling>` to the database requires a username,
|
||||
password and connection string. Locate your Oracle Database `user name and
|
||||
password <https://www.youtube.com/watch?v=WDJacg0NuLo>`_ and the database
|
||||
:ref:`connection string <connstr>`, and use them in ``query.py``. For
|
||||
cx_Oracle, 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 :ref:`cursor <cursorobj>` is the object that allows statements to be
|
||||
executed and results (if any) fetched.
|
||||
|
||||
The data values in ``managerId`` and ``firstName`` are 'bound' to the statement
|
||||
placeholder 'bind variables' ``:mid`` and ``:fn`` when the statement is
|
||||
executed. This separates the statement text from the data, which helps avoid
|
||||
SQL Injection security risks. :ref:`Binding <bind>` is also important for
|
||||
performance and scalability.
|
||||
|
||||
The cursor allows rows to be iterated over and displayed.
|
||||
|
||||
Run the script::
|
||||
|
||||
python query.py
|
||||
|
||||
The output is::
|
||||
|
||||
('Peter', 'Hall')
|
||||
('Peter', 'Tucker')
|
||||
|
||||
Examples and Tutorials
|
||||
----------------------
|
||||
|
||||
Runnable examples are in the `GitHub samples directory
|
||||
<https://github.com/oracle/python-cx_Oracle/tree/main/samples>`__. A `Python
|
||||
cx_Oracle tutorial
|
||||
<https://oracle.github.io/python-cx_Oracle/samples/tutorial/Python-and-Oracle-Database-Scripting-for-the-Future.html>`__
|
||||
is also available.
|
|
@ -1,317 +0,0 @@
|
|||
.. _jsondatatype:
|
||||
|
||||
*******************************
|
||||
Working with the JSON Data Type
|
||||
*******************************
|
||||
|
||||
.. note::
|
||||
|
||||
**cx_Oracle has a major new release under a new name and homepage**
|
||||
`python-oracledb <https://oracle.github.io/python-oracledb/>`__.
|
||||
|
||||
**New projects should install python-oracledb instead of cx_Oracle.**
|
||||
|
||||
Native support for JSON data was introduced in Oracle Database 12c. You can
|
||||
use JSON with relational database features, including transactions, indexing,
|
||||
declarative querying, and views. You can project JSON data relationally,
|
||||
making it available for relational processes and tools. Also see
|
||||
:ref:`Simple Oracle Document Access (SODA) <sodausermanual>`, which allows
|
||||
access to JSON documents through a set of NoSQL-style APIs.
|
||||
|
||||
Prior to Oracle Database 21, JSON in relational tables is stored as BLOB, CLOB
|
||||
or VARCHAR2 data, allowing easy access with cx_Oracle. Oracle Database 21
|
||||
introduced a dedicated JSON data type with a new `binary storage format
|
||||
<https://blogs.oracle.com/jsondb/osonformat>`__ that improves performance and
|
||||
functionality. To use the new dedicated JSON type, the Oracle Database and
|
||||
Oracle Client libraries must be version 21, or later. Also cx_Oracle must be
|
||||
8.1, or later.
|
||||
|
||||
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>`__.
|
||||
|
||||
In Oracle Database 21, to create a table with a column called ``JSON_DATA`` for
|
||||
JSON data:
|
||||
|
||||
.. code-block:: sql
|
||||
|
||||
create table customers (
|
||||
id integer not null primary key,
|
||||
json_data json
|
||||
);
|
||||
|
||||
For older Oracle Database versions the syntax is:
|
||||
|
||||
.. code-block:: sql
|
||||
|
||||
create table customers (
|
||||
id integer not null primary key,
|
||||
json_data blob check (json_data is json)
|
||||
);
|
||||
|
||||
The check constraint with the clause ``IS JSON`` ensures only JSON data is
|
||||
stored in that column.
|
||||
|
||||
The older syntax can still be used in Oracle Database 21, 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 Oracle Database 21 and Oracle Client 21 with cx_Oracle 8.1 (or later),
|
||||
you can insert by binding as shown below:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import datetime
|
||||
|
||||
json_data = [
|
||||
2.78,
|
||||
True,
|
||||
'Ocean Beach',
|
||||
b'Some bytes',
|
||||
{'keyA': 1, 'KeyB': 'Melbourne'},
|
||||
datetime.date.today()
|
||||
]
|
||||
|
||||
var = cursor.var(cx_Oracle.DB_TYPE_JSON)
|
||||
var.setvalue(0, json_data)
|
||||
cursor.execute("insert into customers values (:1, :2)", [123, var])
|
||||
|
||||
# or these two lines can replace the three previous lines
|
||||
cursor.setinputsizes(None, cx_Oracle.DB_TYPE_JSON)
|
||||
cursor.execute("insert into customers values (:1, :2)", [123, json_data])
|
||||
|
||||
Fetching with:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
for row in cursor.execute("SELECT c.json_data FROM customers c"):
|
||||
print(row)
|
||||
|
||||
gives output like::
|
||||
|
||||
([Decimal('2.78'), True, 'Ocean Beach',
|
||||
b'Some bytes',
|
||||
{'keyA': Decimal('1'), 'KeyB': 'Melbourne'},
|
||||
datetime.datetime(2020, 12, 2, 0, 0)],)
|
||||
|
||||
With the older BLOB storage, or to insert JSON strings, use:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import json
|
||||
|
||||
customer_data = dict(name="Rod", dept="Sales", location="Germany")
|
||||
cursor.execute("insert into customers (id, json_data) values (:1, :2)",
|
||||
[1, json.dumps(customer_data)])
|
||||
|
||||
|
||||
IN Bind Type Mapping
|
||||
====================
|
||||
|
||||
When binding to a JSON value, the type parameter for the variable must be
|
||||
specified as :data:`cx_Oracle.DB_TYPE_JSON`. Python values are converted to
|
||||
JSON values as shown in the following table. The 'SQL Equivalent' syntax can
|
||||
be used in SQL INSERT and UPDATE statements if specific attribute types are
|
||||
needed but there is no direct mapping from Python.
|
||||
|
||||
.. list-table::
|
||||
:header-rows: 1
|
||||
:widths: 1 1 1
|
||||
:align: left
|
||||
|
||||
* - Python Type or Value
|
||||
- JSON Attribute Type or Value
|
||||
- SQL Equivalent Example
|
||||
* - None
|
||||
- null
|
||||
- NULL
|
||||
* - True
|
||||
- true
|
||||
- n/a
|
||||
* - False
|
||||
- false
|
||||
- n/a
|
||||
* - int
|
||||
- NUMBER
|
||||
- json_scalar(1)
|
||||
* - float
|
||||
- NUMBER
|
||||
- json_scalar(1)
|
||||
* - decimal.Decimal
|
||||
- NUMBER
|
||||
- json_scalar(1)
|
||||
* - str
|
||||
- VARCHAR2
|
||||
- json_scalar('String')
|
||||
* - datetime.date
|
||||
- TIMESTAMP
|
||||
- json_scalar(to_timestamp('2020-03-10', 'YYYY-MM-DD'))
|
||||
* - datetime.datetime
|
||||
- TIMESTAMP
|
||||
- json_scalar(to_timestamp('2020-03-10', 'YYYY-MM-DD'))
|
||||
* - bytes
|
||||
- RAW
|
||||
- json_scalar(utl_raw.cast_to_raw('A raw value'))
|
||||
* - list
|
||||
- Array
|
||||
- json_array(1, 2, 3 returning json)
|
||||
* - dict
|
||||
- Object
|
||||
- json_object(key 'Fred' value json_scalar(5), key 'George' value json_scalar('A string') returning json)
|
||||
* - n/a
|
||||
- CLOB
|
||||
- json_scalar(to_clob('A short CLOB'))
|
||||
* - n/a
|
||||
- BLOB
|
||||
- json_scalar(to_blob(utl_raw.cast_to_raw('A short BLOB')))
|
||||
* - n/a
|
||||
- DATE
|
||||
- json_scalar(to_date('2020-03-10', 'YYYY-MM-DD'))
|
||||
* - n/a
|
||||
- INTERVAL YEAR TO MONTH
|
||||
- json_scalar(to_yminterval('+5-9'))
|
||||
* - n/a
|
||||
- INTERVAL DAY TO SECOND
|
||||
- json_scalar(to_dsinterval('P25DT8H25M'))
|
||||
* - n/a
|
||||
- BINARY_DOUBLE
|
||||
- json_scalar(to_binary_double(25))
|
||||
* - n/a
|
||||
- BINARY_FLOAT
|
||||
- json_scalar(to_binary_float(15.5))
|
||||
|
||||
An example of creating a CLOB attribute with key ``mydocument`` in a JSON column
|
||||
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'])
|
||||
|
||||
When `mytab` is queried in cx_Oracle, 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
|
||||
===============================
|
||||
|
||||
When getting Oracle Database 21 JSON values from the database, the following
|
||||
attribute mapping occurs:
|
||||
|
||||
.. list-table::
|
||||
:header-rows: 1
|
||||
:widths: 1 1
|
||||
:align: left
|
||||
|
||||
* - Database JSON Attribute Type or Value
|
||||
- Python Type or Value
|
||||
* - null
|
||||
- None
|
||||
* - false
|
||||
- False
|
||||
* - true
|
||||
- True
|
||||
* - NUMBER
|
||||
- decimal.Decimal
|
||||
* - VARCHAR2
|
||||
- str
|
||||
* - RAW
|
||||
- bytes
|
||||
* - CLOB
|
||||
- str
|
||||
* - BLOB
|
||||
- bytes
|
||||
* - DATE
|
||||
- datetime.datetime
|
||||
* - TIMESTAMP
|
||||
- datetime.datetime
|
||||
* - INTERVAL YEAR TO MONTH
|
||||
- not supported
|
||||
* - INTERVAL DAY TO SECOND
|
||||
- datetime.timedelta
|
||||
* - BINARY_DOUBLE
|
||||
- float
|
||||
* - BINARY_FLOAT
|
||||
- float
|
||||
* - Arrays
|
||||
- list
|
||||
* - Objects
|
||||
- dict
|
||||
|
||||
SQL/JSON Path Expressions
|
||||
=========================
|
||||
|
||||
Oracle Database provides SQL access to JSON data using SQL/JSON path
|
||||
expressions. A path expression selects zero or more JSON values that match, or
|
||||
satisfy, it. Path expressions can use wildcards and array ranges. A simple
|
||||
path expression is ``$.friends`` which is the value of the JSON field
|
||||
``friends``.
|
||||
|
||||
For example, the previously created ``customers`` table with JSON column
|
||||
``json_data`` can be queried like:
|
||||
|
||||
.. code-block:: sql
|
||||
|
||||
select c.json_data.location FROM customers c
|
||||
|
||||
With the JSON ``'{"name":"Rod","dept":"Sales","location":"Germany"}'`` stored
|
||||
in the table, the queried value would be ``Germany``.
|
||||
|
||||
The JSON_EXISTS functions tests for the existence of a particular value within
|
||||
some JSON data. To look for JSON entries that have a ``location`` field:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
for blob, in cursor.execute("""
|
||||
select json_data
|
||||
from customers
|
||||
where json_exists(json_data, '$.location')"""):
|
||||
data = json.loads(blob.read())
|
||||
print(data)
|
||||
|
||||
This query might display::
|
||||
|
||||
{'name': 'Rod', 'dept': 'Sales', 'location': 'Germany'}
|
||||
|
||||
The SQL/JSON functions ``JSON_VALUE`` and ``JSON_QUERY`` can also be used.
|
||||
|
||||
Note that the default error-handling behavior for these functions is
|
||||
``NULL ON ERROR``, which means that no value is returned if an error occurs.
|
||||
To ensure that an error is raised, use ``ERROR ON ERROR``.
|
||||
|
||||
For more information, see `SQL/JSON Path Expressions
|
||||
<https://www.oracle.com/pls/topic/lookup?ctx=dblatest&
|
||||
id=GUID-2DC05D71-3D62-4A14-855F-76E054032494>`__
|
||||
in the Oracle JSON Developer's Guide.
|
||||
|
||||
|
||||
Accessing Relational Data as JSON
|
||||
=================================
|
||||
|
||||
In Oracle Database 12.2, or later, the `JSON_OBJECT
|
||||
<https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-1EF347AE-7FDA-4B41-AFE0-DD5A49E8B370>`__
|
||||
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""",
|
||||
[50]);
|
||||
for row in cursor:
|
||||
print(row)
|
||||
|
||||
This produces::
|
||||
|
||||
('{"deptId":10,"name":"Administration"}',)
|
||||
('{"deptId":20,"name":"Marketing"}',)
|
||||
('{"deptId":30,"name":"Purchasing"}',)
|
||||
('{"deptId":40,"name":"Human Resources"}',)
|
|
@ -1,207 +0,0 @@
|
|||
.. _lobdata:
|
||||
|
||||
************************
|
||||
Using CLOB and BLOB Data
|
||||
************************
|
||||
|
||||
.. note::
|
||||
|
||||
**cx_Oracle has a major new release under a new name and homepage**
|
||||
`python-oracledb <https://oracle.github.io/python-oracledb/>`__.
|
||||
|
||||
**New projects should install python-oracledb instead of cx_Oracle.**
|
||||
|
||||
Oracle Database uses :ref:`lobobj` to store large data such as text, images,
|
||||
videos and other multimedia formats. The maximum size of a LOB is limited to
|
||||
the size of the tablespace storing it.
|
||||
|
||||
There are four types of LOB (large object):
|
||||
|
||||
* BLOB - Binary Large Object, used for storing binary data. cx_Oracle uses
|
||||
the type :attr:`cx_Oracle.DB_TYPE_BLOB`.
|
||||
* CLOB - Character Large Object, used for string strings in the database
|
||||
character set format. cx_Oracle uses the type
|
||||
:attr:`cx_Oracle.DB_TYPE_CLOB`.
|
||||
* NCLOB - National Character Large Object, used for string strings in the
|
||||
national character set format. cx_Oracle uses the type
|
||||
:attr:`cx_Oracle.DB_TYPE_NCLOB`.
|
||||
* BFILE - External Binary File, used for referencing a file stored on the
|
||||
host operating system outside of the database. cx_Oracle uses the type
|
||||
:attr:`cx_Oracle.DB_TYPE_BFILE`.
|
||||
|
||||
LOBs can be streamed to, and from, Oracle Database.
|
||||
|
||||
LOBs up to 1 GB in length can be also be handled directly as strings or bytes in
|
||||
cx_Oracle. This makes LOBs easy to work with, and has significant performance
|
||||
benefits over streaming. However it requires the entire LOB data to be present
|
||||
in Python memory, which may not be possible.
|
||||
|
||||
See `GitHub <https://github.com/oracle/python-cx_Oracle/tree/main/samples>`__ for LOB examples.
|
||||
|
||||
|
||||
Simple Insertion of LOBs
|
||||
------------------------
|
||||
|
||||
Consider a table with CLOB and BLOB columns:
|
||||
|
||||
.. code-block:: sql
|
||||
|
||||
CREATE TABLE lob_tbl (
|
||||
id NUMBER,
|
||||
c CLOB,
|
||||
b BLOB
|
||||
);
|
||||
|
||||
With cx_Oracle, LOB data can be inserted in the table by binding strings or
|
||||
bytes as needed:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
with open('example.txt', 'r') as f:
|
||||
text_data = f.read()
|
||||
|
||||
with open('image.png', 'rb') as f:
|
||||
img_data = f.read()
|
||||
|
||||
cursor.execute("""
|
||||
insert into lob_tbl (id, c, b)
|
||||
values (:lobid, :clobdata, :blobdata)""",
|
||||
lobid=10, clobdata=text_data, blobdata=img_data)
|
||||
|
||||
Note that with this approach, LOB data is limited to 1 GB in size.
|
||||
|
||||
.. _directlobs:
|
||||
|
||||
Fetching LOBs as Strings and Bytes
|
||||
----------------------------------
|
||||
|
||||
CLOBs and BLOBs smaller than 1 GB can queried from the database directly as
|
||||
strings and bytes. This can be much faster than streaming.
|
||||
|
||||
A :attr:`Connection.outputtypehandler` or :attr:`Cursor.outputtypehandler` needs
|
||||
to be used as shown in this example:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
def output_type_handler(cursor, name, default_type, size, precision, scale):
|
||||
if default_type == cx_Oracle.DB_TYPE_CLOB:
|
||||
return cursor.var(cx_Oracle.DB_TYPE_LONG, arraysize=cursor.arraysize)
|
||||
if default_type == cx_Oracle.DB_TYPE_BLOB:
|
||||
return cursor.var(cx_Oracle.DB_TYPE_LONG_RAW, arraysize=cursor.arraysize)
|
||||
|
||||
id_val = 1
|
||||
text_data = "The quick brown fox jumps over the lazy dog"
|
||||
binary_data = b"Some binary data"
|
||||
cursor.execute("insert into lob_tbl (id, c, b) values (:1, :2, :3)",
|
||||
[id_val, text_data, binary_data])
|
||||
|
||||
connection.outputtypehandler = output_type_handler
|
||||
cursor.execute("select c, b from lob_tbl where id = :1", [id_val])
|
||||
clob_data, blob_data = cursor.fetchone()
|
||||
print("CLOB length:", len(clob_data))
|
||||
print("CLOB data:", clob_data)
|
||||
print("BLOB length:", len(blob_data))
|
||||
print("BLOB data:", blob_data)
|
||||
|
||||
This displays::
|
||||
|
||||
CLOB length: 43
|
||||
CLOB data: The quick brown fox jumps over the lazy dog
|
||||
BLOB length: 16
|
||||
BLOB data: b'Some binary data'
|
||||
|
||||
|
||||
Streaming LOBs (Read)
|
||||
---------------------
|
||||
|
||||
Without the output type handler, the CLOB and BLOB values are fetched as
|
||||
:ref:`LOB objects<lobobj>`. The size of the LOB object can be obtained by
|
||||
calling :meth:`LOB.size()` and the data can be read by calling
|
||||
:meth:`LOB.read()`:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
id_val = 1
|
||||
text_data = "The quick brown fox jumps over the lazy dog"
|
||||
binary_data = b"Some binary data"
|
||||
cursor.execute("insert into lob_tbl (id, c, b) values (:1, :2, :3)",
|
||||
[id_val, text_data, binary_data])
|
||||
|
||||
cursor.execute("select b, c from lob_tbl where id = :1", [id_val])
|
||||
b, c = cursor.fetchone()
|
||||
print("CLOB length:", c.size())
|
||||
print("CLOB data:", c.read())
|
||||
print("BLOB length:", b.size())
|
||||
print("BLOB data:", b.read())
|
||||
|
||||
This approach produces the same results as the previous example but it will
|
||||
perform more slowly because it requires more :ref:`round-trips <roundtrips>` to
|
||||
Oracle Database and has higher overhead. It is needed, however, if the LOB data
|
||||
cannot be fetched as one block of data from the server.
|
||||
|
||||
To stream the BLOB column, the :meth:`LOB.read()` method can be called
|
||||
repeatedly until all of the data has been read, as shown below:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
cursor.execute("select b from lob_tbl where id = :1", [10])
|
||||
blob, = cursor.fetchone()
|
||||
offset = 1
|
||||
num_bytes_in_chunk = 65536
|
||||
with open("image.png", "wb") as f:
|
||||
while True:
|
||||
data = blob.read(offset, num_bytes_in_chunk)
|
||||
if data:
|
||||
f.write(data)
|
||||
if len(data) < num_bytes_in_chunk:
|
||||
break
|
||||
offset += len(data)
|
||||
|
||||
|
||||
Streaming LOBs (Write)
|
||||
----------------------
|
||||
|
||||
If a row containing a LOB is being inserted or updated, and the quantity of
|
||||
data that is to be inserted or updated cannot fit in a single block of data,
|
||||
the data can be streamed using the method :meth:`LOB.write()` instead as shown
|
||||
in the following code:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
id_val = 9
|
||||
lob_var = cursor.var(cx_Oracle.DB_TYPE_BLOB)
|
||||
cursor.execute("""
|
||||
insert into lob_tbl (id, b)
|
||||
values (:1, empty_blob())
|
||||
returning b into :2""", [id_val, lob_var])
|
||||
blob, = lobVar.getvalue()
|
||||
offset = 1
|
||||
num_bytes_in_chunk = 65536
|
||||
with open("image.png", "rb") as f:
|
||||
while True:
|
||||
data = f.read(num_bytes_in_chunk)
|
||||
if data:
|
||||
blob.write(data, offset)
|
||||
if len(data) < num_bytes_in_chunk:
|
||||
break
|
||||
offset += len(data)
|
||||
connection.commit()
|
||||
|
||||
|
||||
Temporary LOBs
|
||||
--------------
|
||||
|
||||
All of the examples shown thus far have made use of permanent LOBs. These are
|
||||
LOBs that are stored in the database. Oracle also supports temporary LOBs that
|
||||
are not stored in the database but can be used to pass large quantities of
|
||||
data. These LOBs use space in the temporary tablespace until all variables
|
||||
referencing them go out of scope or the connection in which they are created is
|
||||
explicitly closed.
|
||||
|
||||
When calling PL/SQL procedures with data that exceeds 32,767 bytes in length,
|
||||
cx_Oracle automatically creates a temporary LOB internally and passes that
|
||||
value through to the procedure. If the data that is to be passed to the
|
||||
procedure exceeds that which can fit in a single block of data, however, you
|
||||
can use the method :meth:`Connection.createlob()` to create a temporary LOB.
|
||||
This LOB can then be read and written just like in the examples shown above for
|
||||
persistent LOBs.
|
|
@ -1,388 +0,0 @@
|
|||
.. _plsqlexecution:
|
||||
|
||||
****************
|
||||
PL/SQL Execution
|
||||
****************
|
||||
|
||||
.. note::
|
||||
|
||||
**cx_Oracle has a major new release under a new name and homepage**
|
||||
`python-oracledb <https://oracle.github.io/python-oracledb/>`__.
|
||||
|
||||
**New projects should install python-oracledb instead of cx_Oracle.**
|
||||
|
||||
PL/SQL stored procedures, functions and anonymous blocks can be called from
|
||||
cx_Oracle.
|
||||
|
||||
.. _plsqlproc:
|
||||
|
||||
PL/SQL Stored Procedures
|
||||
------------------------
|
||||
|
||||
The :meth:`Cursor.callproc()` method is used to call PL/SQL procedures.
|
||||
|
||||
If a procedure with the following definition exists:
|
||||
|
||||
.. code-block:: sql
|
||||
|
||||
create or replace procedure myproc (
|
||||
a_Value1 number,
|
||||
a_Value2 out number
|
||||
) as
|
||||
begin
|
||||
a_Value2 := a_Value1 * 2;
|
||||
end;
|
||||
|
||||
then the following Python code can be used to call it:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
out_val = cursor.var(int)
|
||||
cursor.callproc('myproc', [123, out_val])
|
||||
print(out_val.getvalue()) # will print 246
|
||||
|
||||
Calling :meth:`Cursor.callproc()` actually generates an anonymous PL/SQL block
|
||||
as shown below, which is then executed:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
cursor.execute("begin myproc(:1,:2); end;", [123, out_val])
|
||||
|
||||
See :ref:`bind` for information on binding.
|
||||
|
||||
|
||||
.. _plsqlfunc:
|
||||
|
||||
PL/SQL Stored Functions
|
||||
-----------------------
|
||||
|
||||
The :meth:`Cursor.callfunc()` method is used to call PL/SQL functions.
|
||||
|
||||
The ``returnType`` parameter for :meth:`~Cursor.callfunc()` is
|
||||
expected to be a Python type, one of the :ref:`cx_Oracle types <types>` or
|
||||
an :ref:`Object Type <objecttype>`.
|
||||
|
||||
If a function with the following definition exists:
|
||||
|
||||
.. code-block:: sql
|
||||
|
||||
create or replace function myfunc (
|
||||
a_StrVal varchar2,
|
||||
a_NumVal number
|
||||
) return number as
|
||||
begin
|
||||
return length(a_StrVal) + a_NumVal * 2;
|
||||
end;
|
||||
|
||||
then the following Python code can be used to call it:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
return_val = cursor.callfunc("myfunc", int, ["a string", 15])
|
||||
print(return_val) # will print 38
|
||||
|
||||
A more complex example that returns a spatial (SDO) object can be seen below.
|
||||
First, the SQL statements necessary to set up the example:
|
||||
|
||||
.. code-block:: sql
|
||||
|
||||
create table MyPoints (
|
||||
id number(9) not null,
|
||||
point sdo_point_type not null
|
||||
);
|
||||
|
||||
insert into MyPoints values (1, sdo_point_type(125, 375, 0));
|
||||
|
||||
create or replace function spatial_queryfn (
|
||||
a_Id number
|
||||
) return sdo_point_type is
|
||||
t_Result sdo_point_type;
|
||||
begin
|
||||
select point
|
||||
into t_Result
|
||||
from MyPoints
|
||||
where Id = a_Id;
|
||||
|
||||
return t_Result;
|
||||
end;
|
||||
/
|
||||
|
||||
The Python code that will call this procedure looks as follows:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
obj_type = connection.gettype("SDO_POINT_TYPE")
|
||||
cursor = connection.cursor()
|
||||
return_val = cursor.callfunc("spatial_queryfn", obj_type, [1])
|
||||
print(f"({return_val.X}, {return_val.Y}, {return_val.Z})")
|
||||
# will print (125, 375, 0)
|
||||
|
||||
See :ref:`bind` for information on binding.
|
||||
|
||||
|
||||
Anonymous PL/SQL Blocks
|
||||
-----------------------
|
||||
|
||||
An anonymous PL/SQL block can be called as shown:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
var = cursor.var(int)
|
||||
cursor.execute("""
|
||||
begin
|
||||
:out_val := length(:in_val);
|
||||
end;""", in_val="A sample string", out_val=var)
|
||||
print(var.getvalue()) # will print 15
|
||||
|
||||
See :ref:`bind` for information on binding.
|
||||
|
||||
|
||||
Creating Stored Procedures and Packages
|
||||
---------------------------------------
|
||||
|
||||
To create PL/SQL stored procedures and packages, use :meth:`Cursor.execute()`
|
||||
with a SQL CREATE command.
|
||||
|
||||
Creation warning messages can be found from database views like USER_ERRORS.
|
||||
|
||||
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 (a in number) as
|
||||
begin
|
||||
WRONG WRONG WRONG
|
||||
end;""")
|
||||
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::
|
||||
|
||||
PLS-00103: Encountered the symbol "WRONG" when expecting one of the following:
|
||||
|
||||
:= . ( @ % ;
|
||||
|
||||
|
||||
Using DBMS_OUTPUT
|
||||
-----------------
|
||||
|
||||
The standard way to print output from PL/SQL is with the package `DBMS_OUTPUT
|
||||
<https://www.oracle.com/pls/topic/lookup?ctx=dblatest&
|
||||
id=GUID-C1400094-18D5-4F36-A2C9-D28B0E12FD8C>`__. Note, PL/SQL code that uses
|
||||
``DBMS_OUTPUT`` runs to completion before any output is available to the user.
|
||||
Also, other database connections cannot access the buffer.
|
||||
|
||||
To use DBMS_OUTPUT:
|
||||
|
||||
* Call the PL/SQL procedure ``DBMS_OUTPUT.ENABLE()`` to enable output to be
|
||||
buffered for the connection.
|
||||
* Execute some PL/SQL that calls ``DBMS_OUTPUT.PUT_LINE()`` to put text in the
|
||||
buffer.
|
||||
* Call ``DBMS_OUTPUT.GET_LINE()`` or ``DBMS_OUTPUT.GET_LINES()`` repeatedly to
|
||||
fetch the text from the buffer until there is no more output.
|
||||
|
||||
|
||||
For example:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# enable DBMS_OUTPUT
|
||||
cursor.callproc("dbms_output.enable")
|
||||
|
||||
# execute some PL/SQL that calls DBMS_OUTPUT.PUT_LINE
|
||||
cursor.execute("""
|
||||
begin
|
||||
dbms_output.put_line('This is the cx_Oracle manual');
|
||||
dbms_output.put_line('Demonstrating how to use DBMS_OUTPUT');
|
||||
end;""")
|
||||
|
||||
# tune this size for your application
|
||||
chunk_size = 100
|
||||
|
||||
# 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:
|
||||
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
|
||||
|
||||
This will produce the following output::
|
||||
|
||||
This is the cx_Oracle manual
|
||||
Demonstrating use of DBMS_OUTPUT
|
||||
|
||||
An alternative is to call ``DBMS_OUTPUT.GET_LINE()`` once per output line,
|
||||
which may be much slower:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
text_var = cursor.var(str)
|
||||
status_var = cursor.var(int)
|
||||
while True:
|
||||
cursor.callproc("dbms_output.get_line", (text_var, status_var))
|
||||
if status_var.getvalue() != 0:
|
||||
break
|
||||
print(text_var.getvalue())
|
||||
|
||||
Implicit results
|
||||
----------------
|
||||
|
||||
Implicit results permit a Python program to consume cursors returned by a
|
||||
PL/SQL block without the requirement to use OUT REF CURSOR parameters. The
|
||||
method :meth:`Cursor.getimplicitresults()` can be used for this purpose. It
|
||||
requires both the Oracle Client and Oracle Database to be 12.1 or higher.
|
||||
|
||||
An example using implicit results is as shown:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
cursor.execute("""
|
||||
declare
|
||||
cust_cur sys_refcursor;
|
||||
sales_cur sys_refcursor;
|
||||
begin
|
||||
open cust_cur for SELECT * FROM cust_table;
|
||||
dbms_sql.return_result(cust_cur);
|
||||
|
||||
open sales_cur for SELECT * FROM sales_table;
|
||||
dbms_sql.return_result(sales_cur);
|
||||
end;""")
|
||||
|
||||
for implicit_cursor in cursor.getimplicitresults():
|
||||
for row in implicit_cursor:
|
||||
print(row)
|
||||
|
||||
Data from both the result sets are returned::
|
||||
|
||||
(1, 'Tom')
|
||||
(2, 'Julia')
|
||||
(1000, 1, 'BOOKS')
|
||||
(2000, 2, 'FURNITURE')
|
||||
|
||||
.. _ebr:
|
||||
|
||||
Edition-Based Redefinition (EBR)
|
||||
--------------------------------
|
||||
|
||||
Oracle Database's `Edition-Based Redefinition
|
||||
<https://www.oracle.com/pls/topic/lookup?ctx=dblatest&
|
||||
id=GUID-58DE05A0-5DEF-4791-8FA8-F04D11964906>`__ feature enables upgrading of
|
||||
the database component of an application while it is in use, thereby minimizing
|
||||
or eliminating down time. This feature allows multiple versions of views,
|
||||
synonyms, PL/SQL objects and SQL Translation profiles to be used concurrently.
|
||||
Different versions of the database objects are associated with an "edition".
|
||||
|
||||
The simplest way to set an edition is to pass the ``edition`` parameter to
|
||||
:meth:`cx_Oracle.connect()` or :meth:`cx_Oracle.SessionPool()`:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
connection = cx_Oracle.connect(user="hr", password=userpwd,
|
||||
dsn="dbhost.example.com/orclpdb1",
|
||||
edition="newsales", encoding="UTF-8")
|
||||
|
||||
|
||||
The edition could also be set by setting the environment variable
|
||||
``ORA_EDITION`` or by executing the SQL statement:
|
||||
|
||||
.. code-block:: sql
|
||||
|
||||
alter session set edition = <edition name>;
|
||||
|
||||
Regardless of which method is used to set the edition, the value that is in use
|
||||
can be seen by examining the attribute :attr:`Connection.edition`. If no value
|
||||
has been set, the value will be None. This corresponds to the database default
|
||||
edition ``ORA$BASE``.
|
||||
|
||||
Consider an example where one version of a PL/SQL function ``Discount`` is
|
||||
defined in the database default edition ``ORA$BASE`` and the other version of
|
||||
the same function is defined in a user created edition ``DEMO``.
|
||||
|
||||
.. code-block:: sql
|
||||
|
||||
connect <username>/<password>
|
||||
|
||||
-- create function using the database default edition
|
||||
CREATE OR REPLACE FUNCTION Discount(price IN NUMBER) RETURN NUMBER IS
|
||||
BEGIN
|
||||
return price * 0.9;
|
||||
END;
|
||||
/
|
||||
|
||||
A new edition named 'DEMO' is created and the user given permission to use
|
||||
editions. The use of ``FORCE`` is required if the user already contains one or
|
||||
more objects whose type is editionable and that also have non-editioned
|
||||
dependent objects.
|
||||
|
||||
.. code-block:: sql
|
||||
|
||||
connect system/<password>
|
||||
|
||||
CREATE EDITION demo;
|
||||
ALTER USER <username> ENABLE EDITIONS FORCE;
|
||||
GRANT USE ON EDITION demo to <username>;
|
||||
|
||||
The ``Discount`` function for the demo edition is as follows:
|
||||
|
||||
.. code-block:: sql
|
||||
|
||||
connect <username>/<password>
|
||||
|
||||
alter session set edition = demo;
|
||||
|
||||
-- Function for the demo edition
|
||||
CREATE OR REPLACE FUNCTION Discount(price IN NUMBER) RETURN NUMBER IS
|
||||
BEGIN
|
||||
return price * 0.5;
|
||||
END;
|
||||
/
|
||||
|
||||
The Python application can then call the required version of the PL/SQL
|
||||
function as shown:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
connection = cx_Oracle.connect(user=user, password=password,
|
||||
dsn="dbhost.example.com/orclpdb1",
|
||||
encoding="UTF-8")
|
||||
print("Edition is:", repr(connection.edition))
|
||||
|
||||
cursor = connection.cursor()
|
||||
discounted_price = cursor.callfunc("Discount", int, [100])
|
||||
print("Price after discount is:", discounted_price)
|
||||
|
||||
# Use the edition parameter for the connection
|
||||
connection = cx_Oracle.connect(user=user, password=password,
|
||||
dsn="dbhost.example.com/orclpdb1",
|
||||
edition="demo", encoding="UTF-8")
|
||||
print("Edition is:", repr(connection.edition))
|
||||
|
||||
cursor = connection.cursor()
|
||||
discounted_price = cursor.callfunc("Discount", int, [100])
|
||||
print("Price after discount is:", discounted_price)
|
||||
|
||||
The output of the function call for the default and demo edition is as shown::
|
||||
|
||||
Edition is: None
|
||||
Price after discount is: 90
|
||||
Edition is: 'DEMO'
|
||||
Price after discount is: 50
|
|
@ -1,216 +0,0 @@
|
|||
.. _sodausermanual:
|
||||
|
||||
************************************
|
||||
Simple Oracle Document Access (SODA)
|
||||
************************************
|
||||
|
||||
.. note::
|
||||
|
||||
**cx_Oracle has a major new release under a new name and homepage**
|
||||
`python-oracledb <https://oracle.github.io/python-oracledb/>`__.
|
||||
|
||||
**New projects should install python-oracledb instead of cx_Oracle.**
|
||||
|
||||
Overview
|
||||
========
|
||||
|
||||
Oracle Database Simple Oracle Document Access (SODA) allows documents to be
|
||||
inserted, queried, and retrieved from Oracle Database using a set of
|
||||
NoSQL-style cx_Oracle methods. Documents are generally JSON data but they can
|
||||
be any data at all (including video, images, sounds, or other binary content).
|
||||
Documents can be fetched from the database by key lookup or by using
|
||||
query-by-example (QBE) pattern-matching.
|
||||
|
||||
SODA uses a SQL schema to store documents but you do not need to know SQL or
|
||||
how the documents are stored. However, access via SQL does allow use of
|
||||
advanced Oracle Database functionality such as analytics for reporting.
|
||||
|
||||
Oracle SODA implementations are also available in `Node.js
|
||||
<https://oracle.github.io/node-oracledb/doc/api.html#sodaoverview>`__, `Java
|
||||
<https://docs.oracle.com/en/database/oracle/simple-oracle-document-access/java/adsda/index.html>`__,
|
||||
`PL/SQL <https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=ADSDP>`__,
|
||||
`Oracle Call Interface
|
||||
<https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-23206C89-891E-43D7-827C-5C6367AD62FD>`__
|
||||
and via `REST
|
||||
<https://docs.oracle.com/en/database/oracle/simple-oracle-document-access/rest/index.html>`__.
|
||||
|
||||
For general information on SODA, see the `SODA home page
|
||||
<https://docs.oracle.com/en/database/oracle/simple-oracle-document-access/index.html>`__
|
||||
and the Oracle Database `Introduction to Simple Oracle Document Access (SODA)
|
||||
<https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=ADSDI>`__ manual.
|
||||
|
||||
For specific requirements see the cx_Oracle :ref:`SODA requirements <sodarequirements>`.
|
||||
|
||||
cx_Oracle uses the following objects for SODA:
|
||||
|
||||
* :ref:`SODA Database Object <sodadb>`: The top level object for cx_Oracle SODA
|
||||
operations. This is acquired from an Oracle Database connection. A 'SODA
|
||||
database' is an abstraction, allowing access to SODA collections in that
|
||||
'SODA database', which then allow access to documents in those collections.
|
||||
A SODA database is analogous to an Oracle Database user or schema, a
|
||||
collection is analogous to a table, and a document is analogous to a table
|
||||
row with one column for a unique document key, a column for the document
|
||||
content, and other columns for various document attributes.
|
||||
|
||||
* :ref:`SODA Collection Object <sodacoll>`: Represents a collection of SODA
|
||||
documents. By default, collections allow JSON documents to be stored. This
|
||||
is recommended for most SODA users. However optional metadata can set
|
||||
various details about a collection, such as its database storage, whether it
|
||||
should track version and time stamp document components, how such components
|
||||
are generated, and what document types are supported. By default, the name of
|
||||
the Oracle Database table storing a collection is the same as the collection
|
||||
name. Note: do not use SQL to drop the database table, since SODA metadata
|
||||
will not be correctly removed. Use the :meth:`SodaCollection.drop()` method
|
||||
instead.
|
||||
|
||||
* :ref:`SODA Document Object <sodadoc>`: Represents a document. Typically the
|
||||
document content will be JSON. The document has properties including the
|
||||
content, a key, timestamps, and the media type. By default, document keys
|
||||
are automatically generated. See :ref:`SODA Document objects <sodadoc>` for
|
||||
the forms of SodaDoc.
|
||||
|
||||
* :ref:`SODA Document Cursor <sodadoccur>`: A cursor object representing the
|
||||
result of the :meth:`SodaOperation.getCursor()` method from a
|
||||
:meth:`SodaCollection.find()` operation. It can be iterated over to access
|
||||
each SodaDoc.
|
||||
|
||||
* :ref:`SODA Operation Object <sodaop>`: An internal object used with
|
||||
:meth:`SodaCollection.find()` to perform read and write operations on
|
||||
documents. Chained methods set properties on a SodaOperation object which is
|
||||
then used by a terminal method to find, count, replace, or remove documents.
|
||||
This is an internal object that should not be directly accessed.
|
||||
|
||||
|
||||
SODA Examples
|
||||
=============
|
||||
|
||||
Creating and adding documents to a collection can be done as follows:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
soda = connection.getSodaDatabase()
|
||||
|
||||
# create a new SODA collection; this will open an existing collection, if
|
||||
# the name is already in use
|
||||
collection = soda.createCollection("mycollection")
|
||||
|
||||
# insert a document into the collection; for the common case of a JSON
|
||||
# document, the content can be a simple Python dictionary which will
|
||||
# internally be converted to a JSON document
|
||||
content = {'name': 'Matilda', 'address': {'city': 'Melbourne'}}
|
||||
returned_doc = collection.insertOneAndGet(content)
|
||||
key = returned_doc.key
|
||||
print('The key of the new SODA document is: ', key)
|
||||
|
||||
By default, a system generated key is created when documents are inserted.
|
||||
With a known key, you can retrieve a document:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# this will return a dictionary (as was inserted in the previous code)
|
||||
content = collection.find().key(key).getOne().getContent()
|
||||
print(content)
|
||||
|
||||
You can also search for documents using query-by-example syntax:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# Find all documents with names like 'Ma%'
|
||||
print("Names matching 'Ma%'")
|
||||
qbe = {'name': {'$like': 'Ma%'}}
|
||||
for doc in collection.find().filter(qbe).getDocuments():
|
||||
content = doc.getContent()
|
||||
print(content["name"])
|
||||
|
||||
See the `samples directory
|
||||
<https://github.com/oracle/python-cx_Oracle/tree/main/samples>`__
|
||||
for runnable SODA examples.
|
||||
|
||||
|
||||
.. _sodametadatacache:
|
||||
|
||||
Using the SODA Metadata Cache
|
||||
=============================
|
||||
|
||||
SODA metadata can be cached to improve the performance of
|
||||
:meth:`SodaDatabase.createCollection()` and
|
||||
:meth:`SodaDatabase.openCollection()` by reducing :ref:`round-trips
|
||||
<roundtrips>` to the database. Caching is available with Oracle Client 21.3 (or
|
||||
later). The feature is also available in Oracle Client 19 from 19.11 onwards.
|
||||
|
||||
The metadata cache can be turned on when creating a connection pool with
|
||||
:meth:`cx_Oracle.SessionPool()`. Each pool has its own cache:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# Create the session pool
|
||||
pool = cx_Oracle.SessionPool(user="hr", password=userpwd,
|
||||
dsn="dbhost.example.com/orclpdb1",
|
||||
soda_metadata_cache=True)
|
||||
|
||||
The cache is not available for standalone connections. Applications using these
|
||||
should retain and reuse the :ref:`collection <sodacoll>` returned from
|
||||
``createCollection()`` or ``openCollection()`` wherever possible, instead of
|
||||
making repeated calls to those methods.
|
||||
|
||||
The cache is not used by ``createCollection()`` when explicitly passing
|
||||
metadata. In this case, instead of using only ``createCollection()`` and
|
||||
relying on its behavior of opening an existing collection like:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
mymetadata = { . . . }
|
||||
collection = soda.createCollection("mycollection", mymetadata) # open existing or create new collection
|
||||
collection.insertOne(mycontent)
|
||||
|
||||
you will find it more efficient to use logic similar to:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
collection = soda.openCollection("mycollection")
|
||||
if collection is None:
|
||||
mymetadata = { . . . }
|
||||
collection = soda.createCollection("mycollection", mymetadata)
|
||||
collection.insertOne(mycontent)
|
||||
|
||||
If collection metadata changes are made externally, the cache can become
|
||||
invalid. If this happens, the cache can be cleared by calling
|
||||
:meth:`SessionPool.reconfigure()` with ``soda_metadata_cache`` set to `False`,
|
||||
or by setting the attribute :attr:`SessionPool.soda_metadata_cache` to `False`.
|
||||
Use a second call to ``reconfigure()`` or set ``soda_metadata_cache`` to
|
||||
re-enable the cache.
|
||||
|
||||
Committing SODA Work
|
||||
====================
|
||||
|
||||
The general recommendation for SODA applications is to turn on
|
||||
:attr:`~Connection.autocommit` globally:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
connection.autocommit = True
|
||||
|
||||
If your SODA document write operations are mostly independent of each other,
|
||||
this removes the overhead of application transaction management and the need for
|
||||
explicit :meth:`Connection.commit()` calls.
|
||||
|
||||
When deciding how to commit transactions, beware of transactional consistency
|
||||
and performance requirements. If you are using individual SODA calls to insert
|
||||
or update a large number of documents with individual calls, you should turn
|
||||
:attr:`~Connection.autocommit` off and issue a single, explicit
|
||||
:meth:`~Connection.commit()` after all documents have been processed. Also
|
||||
consider using :meth:`SodaCollection.insertMany()` or
|
||||
:meth:`SodaCollection.insertManyAndGet()` which have performance benefits.
|
||||
|
||||
If you are not autocommitting, and one of the SODA operations in your
|
||||
transaction fails, then previous uncommitted operations will not be rolled back.
|
||||
Your application should explicitly roll back the transaction with
|
||||
:meth:`Connection.rollback()` to prevent any later commits from committing a
|
||||
partial transaction.
|
||||
|
||||
Note:
|
||||
|
||||
- SODA DDL operations do not commit an open transaction the way that SQL always does for DDL statements.
|
||||
- When :attr:`~Connection.autocommit` is ``True``, most SODA methods will issue a commit before successful return.
|
||||
- SODA provides optimistic locking, see :meth:`SodaOperation.version()`.
|
||||
- When mixing SODA and relational access, any commit or rollback on the connection will affect all work.
|
|
@ -1,812 +0,0 @@
|
|||
.. _sqlexecution:
|
||||
|
||||
*************
|
||||
SQL Execution
|
||||
*************
|
||||
|
||||
.. note::
|
||||
|
||||
**cx_Oracle has a major new release under a new name and homepage**
|
||||
`python-oracledb <https://oracle.github.io/python-oracledb/>`__.
|
||||
|
||||
**New projects should install python-oracledb instead of cx_Oracle.**
|
||||
|
||||
Executing SQL statements is the primary way in which a Python application
|
||||
communicates with Oracle Database. Statements are executed using the methods
|
||||
:meth:`Cursor.execute()` or :meth:`Cursor.executemany()`. Statements include
|
||||
queries, Data Manipulation Language (DML), and Data Definition Language (DDL).
|
||||
A few other `specialty statements
|
||||
<https://www.oracle.com/pls/topic/lookup?ctx=dblatest&
|
||||
id=GUID-E1749EF5-2264-44DF-99EF-AEBEB943BED6>`__ can also be executed.
|
||||
|
||||
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`.
|
||||
|
||||
cx_Oracle 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-cx_Oracle/blob/main/samples/sample_env.py>`__
|
||||
|
||||
SQL statements should not contain a trailing semicolon (";") or forward slash
|
||||
("/"). This will fail:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
cur.execute("select * from MyTable;")
|
||||
|
||||
This is correct:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
cur.execute("select * from MyTable")
|
||||
|
||||
|
||||
SQL Queries
|
||||
===========
|
||||
|
||||
Queries (statements beginning with SELECT or WITH) can only be executed using
|
||||
the method :meth:`Cursor.execute()`. Rows can then be iterated over, or can be
|
||||
fetched using one of the methods :meth:`Cursor.fetchone()`,
|
||||
:meth:`Cursor.fetchmany()` or :meth:`Cursor.fetchall()`. There is a
|
||||
:ref:`default type mapping <defaultfetchtypes>` to Python types that can be
|
||||
optionally :ref:`overridden <outputtypehandlers>`.
|
||||
|
||||
.. IMPORTANT::
|
||||
|
||||
Interpolating or concatenating user data with SQL statements, for example
|
||||
``cur.execute("SELECT * FROM mytab WHERE mycol = '" + myvar + "'")``, is a security risk
|
||||
and impacts performance. Use :ref:`bind variables <bind>` instead. For
|
||||
example, ``cur.execute("SELECT * FROM mytab WHERE mycol = :mybv", mybv=myvar)``.
|
||||
|
||||
.. _fetching:
|
||||
|
||||
Fetch Methods
|
||||
-------------
|
||||
|
||||
After :meth:`Cursor.execute()`, the cursor is returned as a convenience. This
|
||||
allows code to iterate over rows like:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
cur = connection.cursor()
|
||||
for row in cur.execute("select * from MyTable"):
|
||||
print(row)
|
||||
|
||||
Rows can also be fetched one at a time using the method
|
||||
:meth:`Cursor.fetchone()`:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
cur = connection.cursor()
|
||||
cur.execute("select * from MyTable")
|
||||
while True:
|
||||
row = cur.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`.
|
||||
|
||||
.. 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)
|
||||
|
||||
If all of the rows need to be fetched, and can be contained in memory, the
|
||||
method :meth:`Cursor.fetchall()` can be used.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
cur = connection.cursor()
|
||||
cur.execute("select * from MyTable")
|
||||
rows = cur.fetchall()
|
||||
for row in rows:
|
||||
print(row)
|
||||
|
||||
The fetch methods return data as tuples. To return results as dictionaries, see
|
||||
:ref:`rowfactories`.
|
||||
|
||||
Closing Cursors
|
||||
---------------
|
||||
|
||||
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:
|
||||
|
||||
.. 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
|
||||
resources have been reclaimed by the database. In addition, any attempt to use
|
||||
the variable ``cursor`` outside of the block will simply fail.
|
||||
|
||||
.. _querymetadata:
|
||||
|
||||
Query Column Metadata
|
||||
---------------------
|
||||
|
||||
After executing a query, the column metadata such as column names and data types
|
||||
can be obtained using :attr:`Cursor.description`:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
cur = connection.cursor()
|
||||
cur.execute("select * from MyTable")
|
||||
for column in cur.description:
|
||||
print(column)
|
||||
|
||||
This could result in metadata like::
|
||||
|
||||
('ID', <class 'cx_Oracle.DB_TYPE_NUMBER'>, 39, None, 38, 0, 0)
|
||||
('NAME', <class 'cx_Oracle.DB_TYPE_VARCHAR'>, 20, 20, None, None, 1)
|
||||
|
||||
|
||||
.. _defaultfetchtypes:
|
||||
|
||||
Fetch Data Types
|
||||
----------------
|
||||
|
||||
The following table provides a list of all of the data types that cx_Oracle
|
||||
knows how to fetch. The middle column gives the type that is returned in the
|
||||
:ref:`query metadata <querymetadata>`. The last column gives the type of
|
||||
Python object that is returned by default. Python types can be changed with
|
||||
:ref:`Output Type Handlers <outputtypehandlers>`.
|
||||
|
||||
.. list-table::
|
||||
:header-rows: 1
|
||||
:widths: 1 1 1
|
||||
:align: left
|
||||
|
||||
* - Oracle Database Type
|
||||
- cx_Oracle Database Type
|
||||
- Default Python type
|
||||
* - BFILE
|
||||
- :attr:`cx_Oracle.DB_TYPE_BFILE`
|
||||
- :ref:`cx_Oracle.LOB <lobobj>`
|
||||
* - BINARY_DOUBLE
|
||||
- :attr:`cx_Oracle.DB_TYPE_BINARY_DOUBLE`
|
||||
- float
|
||||
* - BINARY_FLOAT
|
||||
- :attr:`cx_Oracle.DB_TYPE_BINARY_FLOAT`
|
||||
- float
|
||||
* - BLOB
|
||||
- :attr:`cx_Oracle.DB_TYPE_BLOB`
|
||||
- :ref:`cx_Oracle.LOB <lobobj>`
|
||||
* - CHAR
|
||||
- :attr:`cx_Oracle.DB_TYPE_CHAR`
|
||||
- str
|
||||
* - CLOB
|
||||
- :attr:`cx_Oracle.DB_TYPE_CLOB`
|
||||
- :ref:`cx_Oracle.LOB <lobobj>`
|
||||
* - CURSOR
|
||||
- :attr:`cx_Oracle.DB_TYPE_CURSOR`
|
||||
- :ref:`cx_Oracle.Cursor <cursorobj>`
|
||||
* - DATE
|
||||
- :attr:`cx_Oracle.DB_TYPE_DATE`
|
||||
- datetime.datetime
|
||||
* - INTERVAL DAY TO SECOND
|
||||
- :attr:`cx_Oracle.DB_TYPE_INTERVAL_DS`
|
||||
- datetime.timedelta
|
||||
* - JSON
|
||||
- :attr:`cx_Oracle.DB_TYPE_JSON`
|
||||
- dict, list or a scalar value [4]_
|
||||
* - LONG
|
||||
- :attr:`cx_Oracle.DB_TYPE_LONG`
|
||||
- str
|
||||
* - LONG RAW
|
||||
- :attr:`cx_Oracle.DB_TYPE_LONG_RAW`
|
||||
- bytes
|
||||
* - NCHAR
|
||||
- :attr:`cx_Oracle.DB_TYPE_NCHAR`
|
||||
- str
|
||||
* - NCLOB
|
||||
- :attr:`cx_Oracle.DB_TYPE_NCLOB`
|
||||
- :ref:`cx_Oracle.LOB <lobobj>`
|
||||
* - NUMBER
|
||||
- :attr:`cx_Oracle.DB_TYPE_NUMBER`
|
||||
- float or int [1]_
|
||||
* - NVARCHAR2
|
||||
- :attr:`cx_Oracle.DB_TYPE_NVARCHAR`
|
||||
- str
|
||||
* - OBJECT [3]_
|
||||
- :attr:`cx_Oracle.DB_TYPE_OBJECT`
|
||||
- :ref:`cx_Oracle.Object <objecttype>`
|
||||
* - RAW
|
||||
- :attr:`cx_Oracle.DB_TYPE_RAW`
|
||||
- bytes
|
||||
* - ROWID
|
||||
- :attr:`cx_Oracle.DB_TYPE_ROWID`
|
||||
- str
|
||||
* - TIMESTAMP
|
||||
- :attr:`cx_Oracle.DB_TYPE_TIMESTAMP`
|
||||
- datetime.datetime
|
||||
* - TIMESTAMP WITH LOCAL TIME ZONE
|
||||
- :attr:`cx_Oracle.DB_TYPE_TIMESTAMP_LTZ`
|
||||
- datetime.datetime [2]_
|
||||
* - TIMESTAMP WITH TIME ZONE
|
||||
- :attr:`cx_Oracle.DB_TYPE_TIMESTAMP_TZ`
|
||||
- datetime.datetime [2]_
|
||||
* - UROWID
|
||||
- :attr:`cx_Oracle.DB_TYPE_ROWID`
|
||||
- str
|
||||
* - VARCHAR2
|
||||
- :attr:`cx_Oracle.DB_TYPE_VARCHAR`
|
||||
- str
|
||||
|
||||
.. [1] If the precision and scale obtained from query column metadata indicate
|
||||
that the value can be expressed as an integer, the value will be
|
||||
returned as an int. If the column is unconstrained (no precision and
|
||||
scale specified), the value will be returned as a float or an int
|
||||
depending on whether the value itself is an integer. In all other cases
|
||||
the value is returned as a float.
|
||||
.. [2] The timestamps returned are naive timestamps without any time zone
|
||||
information present.
|
||||
.. [3] These include all user-defined types such as VARRAY, NESTED TABLE, etc.
|
||||
|
||||
.. [4] If the JSON is an object, then a dict is returned. If it is an array,
|
||||
then a list is returned. If it is a scalar value, then that particular
|
||||
scalar value is returned.
|
||||
|
||||
|
||||
.. _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. Output type handlers do not affect values returned from
|
||||
:meth:`Cursor.callfunc()` or :meth:`Cursor.callproc()`.
|
||||
|
||||
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, name, defaultType, size, precision, scale)
|
||||
|
||||
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.
|
||||
|
||||
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-cx_Oracle/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 cx_Oracle 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 == cx_Oracle.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-cx_Oracle/blob/main/samples/return_numbers_as_decimals.py>`__
|
||||
|
||||
|
||||
.. _outconverters:
|
||||
|
||||
Changing Query Results with Outconverters
|
||||
-----------------------------------------
|
||||
|
||||
cx_Oracle "outconverters" can be used with :ref:`output type handlers
|
||||
<outputtypehandlers>` to change returned data.
|
||||
|
||||
For example, to make queries return empty strings instead of NULLs:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
def out_converter(value):
|
||||
if value is None:
|
||||
return ''
|
||||
return value
|
||||
|
||||
def output_type_handler(cursor, name, default_type, size, precision, scale):
|
||||
if default_type in (cx_Oracle.DB_TYPE_VARCHAR, cx_Oracle.DB_TYPE_CHAR):
|
||||
return cursor.var(str, size, arraysize=cur.arraysize,
|
||||
outconverter=out_converter)
|
||||
|
||||
connection.outputtypehandler = output_type_handler
|
||||
|
||||
|
||||
.. _rowfactories:
|
||||
|
||||
Changing Query Results with Rowfactories
|
||||
----------------------------------------
|
||||
|
||||
cx_Oracle "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()
|
||||
print(data)
|
||||
|
||||
The output is::
|
||||
|
||||
{'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
|
||||
only one of the similarly named columns will be included in the dictionary:
|
||||
|
||||
.. code-block:: sql
|
||||
|
||||
select
|
||||
cat_name,
|
||||
cats.color as cat_color,
|
||||
dog_name,
|
||||
dogs.color
|
||||
from cats, dogs
|
||||
|
||||
.. _scrollablecursors:
|
||||
|
||||
Scrollable Cursors
|
||||
------------------
|
||||
|
||||
Scrollable cursors enable applications to move backwards, forwards, to skip
|
||||
rows, and to move to a particular row in a query result set. The result set is
|
||||
cached on the database server until the cursor is closed. In contrast, regular
|
||||
cursors are restricted to moving forward.
|
||||
|
||||
A scrollable cursor is created by setting the parameter ``scrollable=True``
|
||||
when creating the cursor. The method :meth:`Cursor.scroll()` is used to move to
|
||||
different locations in the result set.
|
||||
|
||||
Examples are:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
cursor = connection.cursor(scrollable=True)
|
||||
cursor.execute("select * from ChildTable order by ChildId")
|
||||
|
||||
cursor.scroll(mode="last")
|
||||
print("LAST ROW:", cursor.fetchone())
|
||||
|
||||
cursor.scroll(mode="first")
|
||||
print("FIRST ROW:", cursor.fetchone())
|
||||
|
||||
cursor.scroll(8, mode="absolute")
|
||||
print("ROW 8:", cursor.fetchone())
|
||||
|
||||
cursor.scroll(6)
|
||||
print("SKIP 6 ROWS:", cursor.fetchone())
|
||||
|
||||
cursor.scroll(-4)
|
||||
print("SKIP BACK 4 ROWS:", cursor.fetchone())
|
||||
|
||||
.. _fetchobjects:
|
||||
|
||||
Fetching Oracle Database Objects and Collections
|
||||
------------------------------------------------
|
||||
|
||||
Oracle Database named object types and user-defined types can be fetched
|
||||
directly in queries. Each item is represented as a :ref:`Python object
|
||||
<objecttype>` corresponding to the Oracle Database object. This Python object
|
||||
can be traversed to access its elements. Attributes including
|
||||
:attr:`ObjectType.name` and :attr:`ObjectType.iscollection`, and methods
|
||||
including :meth:`Object.aslist` and :meth:`Object.asdict` are available.
|
||||
|
||||
For example, if a table ``mygeometrytab`` contains a column ``geometry`` of
|
||||
Oracle's predefined Spatial object type `SDO_GEOMETRY
|
||||
<https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-683FF8C5-A773-4018-932D-2AF6EC8BC119>`__,
|
||||
then it can be queried and printed:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
cur.execute("select geometry from mygeometrytab")
|
||||
for obj, in cur:
|
||||
dumpobject(obj)
|
||||
|
||||
Where ``dumpobject()`` is defined as:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
def dumpobject(obj, prefix = ""):
|
||||
if obj.type.iscollection:
|
||||
print(prefix, "[")
|
||||
for value in obj.aslist():
|
||||
if isinstance(value, cx_Oracle.Object):
|
||||
dumpobject(value, prefix + " ")
|
||||
else:
|
||||
print(prefix + " ", repr(value))
|
||||
print(prefix, "]")
|
||||
else:
|
||||
print(prefix, "{")
|
||||
for attr in obj.type.attributes:
|
||||
value = getattr(obj, attr.name)
|
||||
if isinstance(value, cx_Oracle.Object):
|
||||
print(prefix + " " + attr.name + ":")
|
||||
dumpobject(value, prefix + " ")
|
||||
else:
|
||||
print(prefix + " " + attr.name + ":", repr(value))
|
||||
print(prefix, "}")
|
||||
|
||||
This might produce output like::
|
||||
|
||||
{
|
||||
SDO_GTYPE: 2003
|
||||
SDO_SRID: None
|
||||
SDO_POINT:
|
||||
{
|
||||
X: 1
|
||||
Y: 2
|
||||
Z: 3
|
||||
}
|
||||
SDO_ELEM_INFO:
|
||||
[
|
||||
1
|
||||
1003
|
||||
3
|
||||
]
|
||||
SDO_ORDINATES:
|
||||
[
|
||||
1
|
||||
1
|
||||
5
|
||||
7
|
||||
]
|
||||
}
|
||||
|
||||
Other information on using Oracle objects is in :ref:`Using Bind Variables
|
||||
<bind>`.
|
||||
|
||||
Performance-sensitive applications should consider using scalar types instead of
|
||||
objects. If you do use objects, avoid calling :meth:`Connection.gettype()`
|
||||
unnecessarily, and avoid objects with large numbers of attributes.
|
||||
|
||||
.. _rowlimit:
|
||||
|
||||
Limiting Rows
|
||||
-------------
|
||||
|
||||
Query data is commonly broken into one or more sets:
|
||||
|
||||
- To give an upper bound on the number of rows that a query has to process,
|
||||
which can help improve database scalability.
|
||||
|
||||
- To perform 'Web pagination' that allows moving from one set of rows to a
|
||||
next, or previous, set on demand.
|
||||
|
||||
- For fetching of all data in consecutive small sets for batch processing.
|
||||
This happens because the number of records is too large for Python to handle
|
||||
at one time.
|
||||
|
||||
The latter can be handled by calling :meth:`Cursor.fetchmany()` with one
|
||||
execution of the SQL query.
|
||||
|
||||
'Web pagination' and limiting the maximum number of rows are discussed in this
|
||||
section. For each 'page' of results, a SQL query is executed to get the
|
||||
appropriate set of rows from a table. Since the query may be executed more
|
||||
than once, make sure to use :ref:`bind variables <bind>` for row numbers and
|
||||
row limits.
|
||||
|
||||
Oracle Database 12c SQL introduced an ``OFFSET`` / ``FETCH`` clause which is
|
||||
similar to the ``LIMIT`` keyword of MySQL. In Python you can fetch a set of
|
||||
rows using:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
myoffset = 0 // do not skip any rows (start at row 1)
|
||||
mymaxnumrows = 20 // get 20 rows
|
||||
|
||||
sql =
|
||||
"""SELECT last_name
|
||||
FROM employees
|
||||
ORDER BY last_name
|
||||
OFFSET :offset ROWS FETCH NEXT :maxnumrows ROWS ONLY"""
|
||||
|
||||
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
|
||||
sometimes involves appending the ``OFFSET`` clause to the 'real' user query. Be
|
||||
very careful to avoid SQL injection security issues.
|
||||
|
||||
For Oracle Database 11g and earlier there are several alternative ways
|
||||
to limit the number of rows returned. The old, canonical paging query
|
||||
is::
|
||||
|
||||
SELECT *
|
||||
FROM (SELECT a.*, ROWNUM AS rnum
|
||||
FROM (YOUR_QUERY_GOES_HERE -- including the order by) a
|
||||
WHERE ROWNUM <= MAX_ROW)
|
||||
WHERE rnum >= MIN_ROW
|
||||
|
||||
Here, ``MIN_ROW`` is the row number of first row and ``MAX_ROW`` is the row
|
||||
number of the last row to return. For example::
|
||||
|
||||
SELECT *
|
||||
FROM (SELECT a.*, ROWNUM AS rnum
|
||||
FROM (SELECT last_name FROM employees ORDER BY last_name) a
|
||||
WHERE ROWNUM <= 20)
|
||||
WHERE rnum >= 1
|
||||
|
||||
This always has an 'extra' column, here called RNUM.
|
||||
|
||||
An alternative and preferred query syntax for Oracle Database 11g uses the
|
||||
analytic ``ROW_NUMBER()`` function. For example to get the 1st to 20th names the
|
||||
query is::
|
||||
|
||||
SELECT last_name FROM
|
||||
(SELECT last_name,
|
||||
ROW_NUMBER() OVER (ORDER BY last_name) AS myr
|
||||
FROM employees)
|
||||
WHERE myr BETWEEN 1 and 20
|
||||
|
||||
Make sure to use :ref:`bind variables <bind>` for the upper and lower limit
|
||||
values.
|
||||
|
||||
.. _crc:
|
||||
|
||||
Client Result Cache
|
||||
-------------------
|
||||
|
||||
Python cx_Oracle 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.
|
||||
|
||||
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>`__
|
||||
``CLIENT_RESULT_CACHE_SIZE`` and ``CLIENT_RESULT_CACHE_LAG``, and then
|
||||
restarting the database. For example, to set the parameters:
|
||||
|
||||
.. code-block:: sql
|
||||
|
||||
SQL> ALTER SYSTEM SET CLIENT_RESULT_CACHE_LAG = 3000 SCOPE=SPFILE;
|
||||
SQL> ALTER SYSTEM SET CLIENT_RESULT_CACHE_SIZE = 64K SCOPE=SPFILE;
|
||||
|
||||
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>`__.
|
||||
|
||||
Tables can then be created, or altered, so repeated queries use CRC. This
|
||||
allows existing applications to use CRC without needing modification. For example:
|
||||
|
||||
.. code-block:: sql
|
||||
|
||||
SQL> CREATE TABLE cities (id number, name varchar2(40)) RESULT_CACHE (MODE FORCE);
|
||||
SQL> ALTER TABLE locations RESULT_CACHE (MODE FORCE);
|
||||
|
||||
Alternatively, hints can be used in SQL statements. For example:
|
||||
|
||||
.. code-block:: sql
|
||||
|
||||
SELECT /*+ result_cache */ postal_code FROM locations
|
||||
|
||||
|
||||
.. _fetching-raw-data:
|
||||
|
||||
Fetching Raw Data
|
||||
-----------------
|
||||
|
||||
Sometimes cx_Oracle may have problems converting data stored in the database to
|
||||
Python strings. This can occur if the data stored in the database doesn't match
|
||||
the character set defined by the database. The `encoding_errors` parameter to
|
||||
:meth:`Cursor.var()` permits the data to be returned with some invalid data
|
||||
replaced, but for additional control the parameter `bypass_decode` can be set
|
||||
to `True` and cx_Oracle will bypass the decode step and return `bytes` instead
|
||||
of `str` for data stored in the database as strings. The data can then be
|
||||
examined and corrected as required. This approach should only be used for
|
||||
troubleshooting and correcting invalid data, not for general use!
|
||||
|
||||
The following sample demonstrates how to use this feature:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# define output type handler
|
||||
def return_strings_as_bytes(cursor, name, default_type, size,
|
||||
precision, scale):
|
||||
if default_type == cx_Oracle.DB_TYPE_VARCHAR:
|
||||
return cursor.var(str, arraysize=cursor.arraysize,
|
||||
bypass_decode=True)
|
||||
|
||||
# set output type handler on cursor before fetching data
|
||||
with connection.cursor() as cursor:
|
||||
cursor.outputtypehandler = return_strings_as_bytes
|
||||
cursor.execute("select content, charset from SomeTable")
|
||||
data = cursor.fetchall()
|
||||
|
||||
This will produce output as::
|
||||
|
||||
[(b'Fianc\xc3\xa9', b'UTF-8')]
|
||||
|
||||
|
||||
Note that last ``\xc3\xa9`` is é in UTF-8. Since this is valid UTF-8 you can then
|
||||
perform a decode on the data (the part that was bypassed):
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
value = data[0][0].decode("UTF-8")
|
||||
|
||||
This will return the value "Fiancé".
|
||||
|
||||
If you want to save ``b'Fianc\xc3\xa9'`` into the database directly without
|
||||
using a Python string, you will need to create a variable using
|
||||
:meth:`Cursor.var()` that specifies the type as
|
||||
:data:`~cx_Oracle.DB_TYPE_VARCHAR` (otherwise the value will be treated as
|
||||
:data:`~cx_Oracle.DB_TYPE_RAW`). The following sample demonstrates this:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
with cx_Oracle.connect(user="hr", password=userpwd,
|
||||
dsn="dbhost.example.com/orclpdb1") as conn:
|
||||
with conn.cursor() cursor:
|
||||
var = cursor.var(cx_Oracle.DB_TYPE_VARCHAR)
|
||||
var.setvalue(0, b"Fianc\xc4\x9b")
|
||||
cursor.execute("""
|
||||
update SomeTable set
|
||||
SomeColumn = :param
|
||||
where id = 1""",
|
||||
param=var)
|
||||
|
||||
.. warning::
|
||||
|
||||
The database will assume that the bytes provided are in the character set
|
||||
expected by the database so only use this for troubleshooting or as
|
||||
directed.
|
||||
|
||||
|
||||
.. _codecerror:
|
||||
|
||||
Querying Corrupt Data
|
||||
---------------------
|
||||
|
||||
If queries fail with the error "codec can't decode byte" when you select data,
|
||||
then:
|
||||
|
||||
* Check your :ref:`character set <globalization>` is correct. Review the
|
||||
:ref:`client and database character sets <findingcharset>`. Check with
|
||||
:ref:`fetching-raw-data`. Consider using UTF-8, if this is appropriate:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
connection = cx_Oracle.connect(user="hr", password=userpwd,
|
||||
dsn="dbhost.example.com/orclpdb1",
|
||||
encoding="UTF-8", nencoding="UTF-8")
|
||||
|
||||
* Check for corrupt data in the database.
|
||||
|
||||
If data really is corrupt, you can pass options to the internal `decode()
|
||||
<https://docs.python.org/3/library/stdtypes.html#bytes.decode>`__ used by
|
||||
cx_Oracle to allow it to be selected and prevent the whole query failing. Do
|
||||
this by creating an :ref:`outputtypehandler <outputtypehandlers>` and setting
|
||||
``encoding_errors``. For example to replace corrupt characters in character
|
||||
columns:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
def output_type_handler(cursor, name, default_type, size, precision, scale):
|
||||
if default_type == cx_Oracle.DB_TYPE_VARCHAR:
|
||||
return cursor.var(default_type, size, arraysize=cursor.arraysize,
|
||||
encoding_errors="replace")
|
||||
|
||||
cursor.outputtypehandler = output_type_handler
|
||||
|
||||
cursor.execute("select column1, column2 from SomeTableWithBadData")
|
||||
|
||||
Other codec behaviors can be chosen for ``encoding_errors``, see `Error Handlers
|
||||
<https://docs.python.org/3/library/codecs.html#error-handlers>`__.
|
||||
|
||||
.. _dml:
|
||||
|
||||
|
||||
INSERT and UPDATE Statements
|
||||
============================
|
||||
|
||||
SQL Data Manipulation Language statements (DML) such as INSERT and UPDATE can
|
||||
easily be executed with cx_Oracle. For example:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
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.
|
||||
|
||||
See :ref:`txnmgmnt` for best practices on committing and rolling back data
|
||||
changes.
|
||||
|
||||
When handling multiple data values, use :meth:`~Cursor.executemany()` for
|
||||
performance. See :ref:`batchstmnt`
|
||||
|
||||
|
||||
Inserting NULLs
|
||||
---------------
|
||||
|
||||
Oracle requires a type, even for null values. When you pass the value None, then
|
||||
cx_Oracle assumes the type is STRING. If this is not the desired type, you can
|
||||
explicitly set it. For example, to insert a null :ref:`Oracle Spatial
|
||||
SDO_GEOMETRY <spatial>` object:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
type_obj = connection.gettype("SDO_GEOMETRY")
|
||||
cur = connection.cursor()
|
||||
cur.setinputsizes(type_obj)
|
||||
cur.execute("insert into sometable values (:1)", [None])
|
|
@ -1,69 +0,0 @@
|
|||
.. _startup:
|
||||
|
||||
*************************************
|
||||
Starting and Stopping Oracle Database
|
||||
*************************************
|
||||
|
||||
.. note::
|
||||
|
||||
**cx_Oracle has a major new release under a new name and homepage**
|
||||
`python-oracledb <https://oracle.github.io/python-oracledb/>`__.
|
||||
|
||||
**New projects should install python-oracledb instead of cx_Oracle.**
|
||||
|
||||
This chapter covers how to start up and shutdown Oracle Database using
|
||||
cx_Oracle.
|
||||
|
||||
===========================
|
||||
Starting Oracle Database Up
|
||||
===========================
|
||||
|
||||
cx_Oracle can start up a database instance. A privileged connection is
|
||||
required. This example shows a script that could be run as the 'oracle'
|
||||
operating system user who administers a local database installation on Linux.
|
||||
It assumes that the environment variable ``ORACLE_SID`` has been set to the SID
|
||||
of the database that should be started:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# the connection must be in PRELIM_AUTH mode to perform startup
|
||||
connection = cx_Oracle.connect(mode=cx_Oracle.SYSDBA | cx_Oracle.PRELIM_AUTH)
|
||||
connection.startup()
|
||||
|
||||
# the following statements must be issued in normal SYSDBA mode
|
||||
connection = cx_Oracle.connect(mode=cx_Oracle.SYSDBA, encoding="UTF-8")
|
||||
cursor = connection.cursor()
|
||||
cursor.execute("alter database mount")
|
||||
cursor.execute("alter database open")
|
||||
|
||||
To start up a remote database, you may need to configure the Oracle Net
|
||||
listener to use `static service registration
|
||||
<https://www.oracle.com/pls/topic/lookup?ctx=dblatest&
|
||||
id=GUID-0203C8FA-A4BE-44A5-9A25-3D1E578E879F>`_
|
||||
by adding a ``SID_LIST_LISTENER`` entry to the database `listener.ora` file.
|
||||
|
||||
|
||||
=============================
|
||||
Shutting Oracle Database Down
|
||||
=============================
|
||||
|
||||
cx_Oracle has the ability to shutdown the database using a privileged
|
||||
connection. This example also assumes that the environment variable
|
||||
``ORACLE_SID`` has been set:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# need to connect as SYSDBA or SYSOPER
|
||||
connection = cx_Oracle.connect(mode=cx_Oracle.SYSDBA)
|
||||
|
||||
# first shutdown() call must specify the mode, if DBSHUTDOWN_ABORT is used,
|
||||
# there is no need for any of the other steps
|
||||
connection.shutdown(mode=cx_Oracle.DBSHUTDOWN_IMMEDIATE)
|
||||
|
||||
# now close and dismount the database
|
||||
cursor = connection.cursor()
|
||||
cursor.execute("alter database close normal")
|
||||
cursor.execute("alter database dismount")
|
||||
|
||||
# perform the final shutdown call
|
||||
connection.shutdown(mode=cx_Oracle.DBSHUTDOWN_FINAL)
|
|
@ -1,175 +0,0 @@
|
|||
.. _tracingsql:
|
||||
|
||||
*********************************
|
||||
Tracing SQL and PL/SQL Statements
|
||||
*********************************
|
||||
|
||||
.. note::
|
||||
|
||||
**cx_Oracle has a major new release under a new name and homepage**
|
||||
`python-oracledb <https://oracle.github.io/python-oracledb/>`__.
|
||||
|
||||
**New projects should install python-oracledb instead of cx_Oracle.**
|
||||
|
||||
Subclass Connections
|
||||
====================
|
||||
|
||||
Subclassing enables applications to add "hooks" for connection and statement
|
||||
execution. This can be used to alter, or log, connection and execution
|
||||
parameters, and to extend cx_Oracle functionality.
|
||||
|
||||
The example below demonstrates subclassing a connection to log SQL execution
|
||||
to a file. This example also shows how connection credentials can be embedded
|
||||
in the custom subclass, so application code does not need to supply them.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
class Connection(cx_Oracle.Connection):
|
||||
log_file_name = "log.txt"
|
||||
|
||||
def __init__(self):
|
||||
connect_string = "hr/hr_password@dbhost.example.com/orclpdb1"
|
||||
self._log("Connect to the database")
|
||||
return super(Connection, self).__init__(connect_string)
|
||||
|
||||
def _log(self, message):
|
||||
with open(self.log_file_name, "a") as f:
|
||||
print(message, file=f)
|
||||
|
||||
def execute(self, sql, parameters):
|
||||
self._log(sql)
|
||||
cursor = self.cursor()
|
||||
try:
|
||||
return cursor.execute(sql, parameters)
|
||||
except cx_Oracle.Error as e:
|
||||
error_obj, = e.args
|
||||
self._log(error_obj.message)
|
||||
raise
|
||||
|
||||
connection = Connection()
|
||||
connection.execute("""
|
||||
select department_name
|
||||
from departments
|
||||
where department_id = :id""", dict(id=270))
|
||||
|
||||
The messages logged in ``log.txt`` are::
|
||||
|
||||
Connect to the database
|
||||
|
||||
select department_name
|
||||
from departments
|
||||
where department_id = :id
|
||||
|
||||
If an error occurs, perhaps due to a missing table, the log file would contain
|
||||
instead::
|
||||
|
||||
Connect to the database
|
||||
|
||||
select department_name
|
||||
from departments
|
||||
where department_id = :id
|
||||
ORA-00942: table or view does not exist
|
||||
|
||||
In production applications be careful not to log sensitive information.
|
||||
|
||||
See `Subclassing.py
|
||||
<https://github.com/oracle/python-cx_Oracle/blob/main/
|
||||
samples/subclassing.py>`__ for an example.
|
||||
|
||||
|
||||
.. _endtoendtracing:
|
||||
|
||||
Oracle Database End-to-End Tracing
|
||||
==================================
|
||||
|
||||
Oracle Database End-to-end application tracing simplifies diagnosing application
|
||||
code flow and performance problems in multi-tier or multi-user environments.
|
||||
|
||||
The connection attributes, :attr:`~Connection.client_identifier`,
|
||||
:attr:`~Connection.clientinfo`, :attr:`~Connection.dbop`,
|
||||
:attr:`~Connection.module` and :attr:`~Connection.action`, set the metadata for
|
||||
end-to-end tracing. You can use data dictionary and ``V$`` views to monitor
|
||||
tracing or use other application tracing utilities.
|
||||
|
||||
The attributes are sent to the database when the next :ref:`round-trip
|
||||
<roundtrips>` to the database occurs, for example when the next SQL statement is
|
||||
executed.
|
||||
|
||||
The attribute values will remain set in connections released back to connection
|
||||
pools. When the application re-acquires a connection from the pool it should
|
||||
initialize the values to a desired state before using that connection.
|
||||
|
||||
The example below shows setting the action, module and client identifier
|
||||
attributes on the connection object:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# Set the tracing metadata
|
||||
connection.client_identifier = "pythonuser"
|
||||
connection.action = "Query Session tracing parameters"
|
||||
connection.module = "End-to-end Demo"
|
||||
|
||||
for row in cursor.execute("""
|
||||
SELECT username, client_identifier, module, action
|
||||
FROM V$SESSION
|
||||
WHERE username = 'SYSTEM'"""):
|
||||
print(row)
|
||||
|
||||
The output will be::
|
||||
|
||||
('SYSTEM', 'pythonuser', 'End-to-end Demo', 'Query Session tracing parameters')
|
||||
|
||||
The values can also be manually set as shown by calling
|
||||
`DBMS_APPLICATION_INFO procedures
|
||||
<https://www.oracle.com/pls/topic/lookup?ctx=dblatest&
|
||||
id=GUID-14484F86-44F2-4B34-B34E-0C873D323EAD>`__
|
||||
or `DBMS_SESSION.SET_IDENTIFIER
|
||||
<https://www.oracle.com/pls/topic/lookup?ctx=dblatest&
|
||||
id=GUID-988EA930-BDFE-4205-A806-E54F05333562>`__. These incur round-trips to
|
||||
the database, however, reducing scalability.
|
||||
|
||||
.. code-block:: sql
|
||||
|
||||
BEGIN
|
||||
DBMS_SESSION.SET_IDENTIFIER('pythonuser');
|
||||
DBMS_APPLICATION_INFO.set_module('End-to-End Demo');
|
||||
DBMS_APPLICATION_INFO.set_action(action_name => 'Query Session tracing parameters');
|
||||
END;
|
||||
|
||||
|
||||
Low Level SQL Tracing in cx_Oracle
|
||||
==================================
|
||||
|
||||
cx_Oracle is implemented using the `ODPI-C <https://oracle.github.io/odpi>`__
|
||||
wrapper on top of the Oracle Client libraries. The ODPI-C tracing capability
|
||||
can be used to log executed cx_Oracle statements to the standard error stream.
|
||||
Before executing Python, set the environment variable ``DPI_DEBUG_LEVEL`` to
|
||||
16.
|
||||
|
||||
At a Windows command prompt, this could be done with::
|
||||
|
||||
set DPI_DEBUG_LEVEL=16
|
||||
|
||||
On Linux, you might use::
|
||||
|
||||
export DPI_DEBUG_LEVEL=16
|
||||
|
||||
After setting the variable, run the Python Script, for example on Linux::
|
||||
|
||||
python end-to-endtracing.py 2> log.txt
|
||||
|
||||
For an application that does a single query, the log file might contain a
|
||||
tracing line consisting of the prefix 'ODPI', a thread identifier, a timestamp,
|
||||
and the SQL statement executed::
|
||||
|
||||
ODPI [26188] 2019-03-26 09:09:03.909: ODPI-C 3.1.1
|
||||
ODPI [26188] 2019-03-26 09:09:03.909: debugging messages initialized at level 16
|
||||
ODPI [26188] 2019-03-26 09:09:09.917: SQL SELECT * FROM jobss
|
||||
Traceback (most recent call last):
|
||||
File "end-to-endtracing.py", line 14, in <module>
|
||||
cursor.execute("select * from jobss")
|
||||
cx_Oracle.DatabaseError: ORA-00942: table or view does not exist
|
||||
|
||||
See `ODPI-C Debugging
|
||||
<https://oracle.github.io/odpi/doc/user_guide/debugging.html>`__ for
|
||||
documentation on ``DPI_DEBUG_LEVEL``.
|
|
@ -1,406 +0,0 @@
|
|||
.. _tuning:
|
||||
|
||||
****************
|
||||
Tuning cx_Oracle
|
||||
****************
|
||||
|
||||
.. note::
|
||||
|
||||
**cx_Oracle has a major new release under a new name and homepage**
|
||||
`python-oracledb <https://oracle.github.io/python-oracledb/>`__.
|
||||
|
||||
**New projects should install python-oracledb instead of cx_Oracle.**
|
||||
|
||||
Some general tuning tips are:
|
||||
|
||||
* Tune your application architecture.
|
||||
|
||||
A general application goal is to reduce the number of :ref:`round-trips
|
||||
<roundtrips>` between cx_Oracle and the database.
|
||||
|
||||
For multi-user applications, make use of connection pooling. Create the pool
|
||||
once during application initialization. Do not oversize the pool, see
|
||||
:ref:`connpool` . Use a session callback function to set session state, see
|
||||
:ref:`Session CallBacks for Setting Pooled Connection State <sessioncallback>`.
|
||||
|
||||
Make use of efficient cx_Oracle functions. For example, to insert
|
||||
multiple rows use :meth:`Cursor.executemany()` instead of
|
||||
:meth:`Cursor.execute()`.
|
||||
|
||||
* Tune your SQL statements. See the `SQL Tuning Guide
|
||||
<https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=TGSQL>`__.
|
||||
|
||||
Use :ref:`bind variables <bind>` to avoid statement reparsing.
|
||||
|
||||
Tune :attr:`Cursor.arraysize` and :attr:`Cursor.prefetchrows` for each query,
|
||||
see :ref:`Tuning Fetch Performance <tuningfetch>`.
|
||||
|
||||
Do simple optimizations like :ref:`limiting the number of rows <rowlimit>` and
|
||||
avoiding selecting columns not used in the application.
|
||||
|
||||
It may be faster to work with simple scalar relational values than to use
|
||||
Oracle Database object types.
|
||||
|
||||
Make good use of PL/SQL to avoid executing many individual statements from
|
||||
cx_Oracle.
|
||||
|
||||
Tune the :ref:`Statement Cache <stmtcache>`.
|
||||
|
||||
Enable :ref:`Client Result Caching <clientresultcache>` for small lookup tables.
|
||||
|
||||
* Tune your database. See the `Database Performance Tuning Guide
|
||||
<https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=TGDBA>`__.
|
||||
|
||||
* 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 `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>`__.
|
||||
|
||||
* Do not commit or rollback unnecessarily. Use :attr:`Connection.autocommit` on
|
||||
the last of a sequence of DML statements.
|
||||
|
||||
.. _tuningfetch:
|
||||
|
||||
Tuning Fetch Performance
|
||||
========================
|
||||
|
||||
To tune queries you can adjust cx_Oracle's internal buffer sizes to improve the
|
||||
speed of fetching rows across the network from the database, and to optimize
|
||||
memory usage. Regardless of which cx_Oracle method 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 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.
|
||||
|
||||
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 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 the
|
||||
code layer that is doing the buffering, and when the buffering occurs. The
|
||||
Oracle Client libraries used by cx_Oracle have separate "execute SQL statement"
|
||||
and "fetch data" calls. Prefetching allows query results to be returned to the
|
||||
application when the successful statement execution acknowledgment is returned
|
||||
from the database. This means that a subsequent internal "fetch data" operation
|
||||
does not always need to make a round-trip to the database because rows are
|
||||
already buffered in the Oracle Client libraries. Reducing round-trips helps
|
||||
performance and scalability. An overhead of prefetching is the need for an
|
||||
additional data copy from Oracle Client's prefetch buffers.
|
||||
|
||||
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. This is because the cost of the extra memory copy from the
|
||||
prefetch buffers when fetching a large quantity of rows or very "wide" rows may
|
||||
outweigh the cost of a round-trip for a single cx_Oracle 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 the starting point to begin your tuning:
|
||||
|
||||
* 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)
|
||||
|
||||
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, start your tuning by setting
|
||||
``arraysize`` to the number of expected rows, and set ``prefetchrows`` to one
|
||||
greater than this value. (Adding one removes the need for a round-trip to check
|
||||
for end-of-fetch). For example, if you are querying 20 rows, perhaps to
|
||||
:ref:`display a page <rowlimit>` of data, set ``prefetchrows`` to 21 and
|
||||
``arraysize`` to 20:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
cur = connection.cursor()
|
||||
|
||||
cur.prefetchrows = 21
|
||||
cur.arraysize = 20
|
||||
|
||||
for row in cur.execute("""
|
||||
SELECT last_name
|
||||
FROM employees
|
||||
ORDER BY last_name
|
||||
OFFSET 0 ROWS FETCH NEXT 20 ROWS ONLY"""):
|
||||
print(row)
|
||||
|
||||
This will return all rows for the query in one round-trip.
|
||||
|
||||
* If you know that a query returns just one row then set :attr:`Cursor.arraysize`
|
||||
to 1 to minimize memory usage. The default prefetch value of 2 allows minimal
|
||||
round-trips for single-row queries:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
cur = connection.cursor()
|
||||
cur.arraysize = 1
|
||||
cur.execute("select * from MyTable where id = 1"):
|
||||
row = cur.fetchone()
|
||||
print(row)
|
||||
|
||||
In cx_Oracle, the ``arraysize`` and ``prefetchrows`` values are only examined
|
||||
when a statement is executed the first time. To change the values, create a new
|
||||
cursor. For example, to change ``arraysize`` for a repeated statement:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
array_sizes = (10, 100, 1000)
|
||||
for size in array_sizes:
|
||||
cursor = connection.cursor()
|
||||
cursor.arraysize = size
|
||||
start = time.time()
|
||||
cursor.execute(sql).fetchall()
|
||||
elapsed = time.time() - start
|
||||
print("Time for", size, elapsed, "seconds")
|
||||
|
||||
There are two cases that will benefit from setting :attr:`Cursor.prefetchrows`
|
||||
to 0:
|
||||
|
||||
* When passing REF CURSORS into PL/SQL packages. Setting ``prefetchrows`` to 0
|
||||
can stop rows being prematurely (and silently) fetched into cx_Oracle's
|
||||
internal buffers, making them 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 cx_Oracle can return them to the application. Setting
|
||||
``prefetchrows`` to 0 helps give a consistent flow of data to the application.
|
||||
|
||||
Prefetching can also be enabled in an external :ref:`oraaccess.xml
|
||||
<optclientfiles>` file, which may be useful for tuning an application when
|
||||
modifying its code is not feasible. Setting the size in ``oraaccess.xml`` will
|
||||
affect the whole application, so it should not be the first tuning choice.
|
||||
|
||||
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()
|
||||
|
||||
Tuning REF CURSORS
|
||||
++++++++++++++++++
|
||||
|
||||
In cx_Oracle, REF CURSORS can also be tuned by setting the values of ``arraysize``
|
||||
and ``prefetchrows``. The prefetchrows value must be set before calling the PL/SQL
|
||||
procedure as 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()
|
||||
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)
|
||||
|
||||
.. _roundtrips:
|
||||
|
||||
Database Round-trips
|
||||
====================
|
||||
|
||||
A round-trip is defined as the trip from the Oracle Client libraries (used by
|
||||
cx_Oracle) to the database and back. Calling each cx_Oracle 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>`__.
|
||||
|
||||
Some general tips for reducing round-trips are:
|
||||
|
||||
* 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 cx_Oracle.
|
||||
* Use scalar types instead of Oracle Database object types.
|
||||
* Avoid overuse of :meth:`Connection.ping()`.
|
||||
* Avoid setting :data:`SessionPool.ping_interval` to 0 or a small value.
|
||||
* When using SODA, 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>`__
|
||||
(AWR) reports show 'SQL*Net roundtrips to/from client' and are useful for
|
||||
finding the overall behavior of a system.
|
||||
|
||||
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:: sql
|
||||
|
||||
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
|
||||
=================
|
||||
|
||||
cx_Oracle'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 cx_Oracle 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 size can be set when creating connection pools or
|
||||
standalone connections. The size can subsequently be changed with
|
||||
:attr:`Connection.stmtcachesize` or :attr:`SessionPool.stmtcachesize`. 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 cx_Oracle. 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.
|
||||
|
||||
Statement caching can be disabled by setting the size to 0. Disabling
|
||||
the cache may be beneficial when the quantity or order of statements
|
||||
causes cache entries to be flushed before they get a chance to be
|
||||
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.
|
||||
|
||||
With connection pools, the effect of changing :attr:`SessionPool.stmtcachesize`
|
||||
after pool creation depends on the Oracle Client version:
|
||||
|
||||
- When using Oracle Client 21 (or later), changing the cache size 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. If it is neccessary to change the size on a
|
||||
connection because it is not being released to the pool, use
|
||||
:data:`Connection.stmtcachesize`.
|
||||
|
||||
- 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. To change the size on a connection, use
|
||||
:data:`Connection.stmtcachesize`.
|
||||
|
||||
When it is inconvenient to pass statement text through an application, the
|
||||
:meth:`Cursor.prepare()` call can be used to avoid statement re-parsing.
|
||||
Subsequent ``execute()`` calls use the value ``None`` instead of the SQL text:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
cur.prepare("select * from dept where deptno = :id order by deptno")
|
||||
|
||||
cur.execute(None, id = 20)
|
||||
res = cur.fetchall()
|
||||
print(res)
|
||||
|
||||
cur.execute(None, id = 10)
|
||||
res = cur.fetchall()
|
||||
print(res)
|
||||
|
||||
Statements passed to :meth:`~Cursor.prepare()` are also stored in the statement
|
||||
cache.
|
||||
|
||||
.. _clientresultcache:
|
||||
|
||||
Client Result Caching
|
||||
=====================
|
||||
|
||||
cx_Oracle 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.
|
||||
|
||||
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>`__
|
||||
``CLIENT_RESULT_CACHE_SIZE`` and ``CLIENT_RESULT_CACHE_LAG``, and then
|
||||
restarting the database, for example:
|
||||
|
||||
.. code-block:: sql
|
||||
|
||||
SQL> ALTER SYSTEM SET CLIENT_RESULT_CACHE_LAG = 3000 SCOPE=SPFILE;
|
||||
SQL> ALTER SYSTEM SET CLIENT_RESULT_CACHE_SIZE = 64K SCOPE=SPFILE;
|
||||
SQL> STARTUP FORCE
|
||||
|
||||
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>`__.
|
||||
|
||||
Tables can then be created, or altered, so repeated queries use CRC. This
|
||||
allows existing applications to use CRC without needing modification. For example:
|
||||
|
||||
.. code-block:: sql
|
||||
|
||||
SQL> CREATE TABLE cities (id number, name varchar2(40)) RESULT_CACHE (MODE FORCE);
|
||||
SQL> ALTER TABLE locations RESULT_CACHE (MODE FORCE);
|
||||
|
||||
Alternatively, hints can be used in SQL statements. For example:
|
||||
|
||||
.. code-block:: sql
|
||||
|
||||
SELECT /*+ result_cache */ postal_code FROM locations
|
|
@ -1,84 +0,0 @@
|
|||
.. _txnmgmnt:
|
||||
|
||||
**********************
|
||||
Transaction Management
|
||||
**********************
|
||||
|
||||
.. note::
|
||||
|
||||
**cx_Oracle has a major new release under a new name and homepage**
|
||||
`python-oracledb <https://oracle.github.io/python-oracledb/>`__.
|
||||
|
||||
**New projects should install python-oracledb instead of cx_Oracle.**
|
||||
|
||||
A database transaction is a grouping of SQL statements that make a logical data
|
||||
change to the database.
|
||||
|
||||
When :meth:`Cursor.execute()` executes a SQL statement, a transaction is
|
||||
started or continued. By default, cx_Oracle 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
|
||||
|
||||
cursor.execute("INSERT INTO mytab (name) VALUES ('John')")
|
||||
connection.commit()
|
||||
|
||||
When a database connection is closed, such as with :meth:`Connection.close()`,
|
||||
or when variables referencing the connection go out of scope, any uncommitted
|
||||
transaction will be rolled back.
|
||||
|
||||
|
||||
Autocommitting
|
||||
==============
|
||||
|
||||
An alternative way to commit is to set the attribute
|
||||
:attr:`~Connection.autocommit` of the connection to ``True``. This ensures all
|
||||
:ref:`DML <dml>` statements (INSERT, UPDATE etc) are committed as they are
|
||||
executed. Unlike :meth:`Connection.commit()`, this does not require an
|
||||
additional :ref:`round-trip <roundtrips>` to the database so it is more
|
||||
efficient when used appropriately.
|
||||
|
||||
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.
|
||||
|
||||
The example below shows a new customer being added to the table ``CUST_TABLE``.
|
||||
The corresponding ``SALES`` table is updated with a purchase of 3000 pens from
|
||||
the customer. The final insert uses autocommit mode to commit both new
|
||||
records:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# Add a new customer
|
||||
id_var = cursor.var(int)
|
||||
connection.autocommit = False # make sure any previous value is off
|
||||
cursor.execute("""
|
||||
INSERT INTO cust_table (name) VALUES ('John')
|
||||
RETURNING id INTO :bvid""", bvid=id_var)
|
||||
|
||||
# Add sales data for the new customer and commit all new values
|
||||
id_val = id_var.getvalue()[0]
|
||||
connection.autocommit = True
|
||||
cursor.execute("INSERT INTO sales_table VALUES (:bvid, 'pens', 3000)",
|
||||
bvid=id_val)
|
||||
|
||||
|
||||
Explicit Transactions
|
||||
=====================
|
||||
|
||||
The method :meth:`Connection.begin()` can be used to explicitly start a local
|
||||
or global transaction.
|
||||
|
||||
Without parameters, this explicitly begins a local transaction; otherwise, this
|
||||
explicitly begins a distributed (global) transaction with the given parameters.
|
||||
See the Oracle documentation for more details.
|
||||
|
||||
Note that in order to make use of global (distributed) transactions, the
|
||||
attributes :attr:`Connection.internal_name` and
|
||||
:attr:`Connection.external_name` attributes must be set.
|
|
@ -1,75 +0,0 @@
|
|||
.. _xmldatatype:
|
||||
|
||||
********************
|
||||
Working with XMLTYPE
|
||||
********************
|
||||
|
||||
.. note::
|
||||
|
||||
**cx_Oracle has a major new release under a new name and homepage**
|
||||
`python-oracledb <https://oracle.github.io/python-oracledb/>`__.
|
||||
|
||||
**New projects should install python-oracledb instead of cx_Oracle.**
|
||||
|
||||
Oracle XMLType columns are fetched as strings by default. This is currently
|
||||
limited to the maximum length of a ``VARCHAR2`` column. To return longer XML
|
||||
values, they must be queried as LOB values instead.
|
||||
|
||||
The examples below demonstrate using XMLType data with cx_Oracle. The
|
||||
following table will be used in these examples:
|
||||
|
||||
.. code-block:: sql
|
||||
|
||||
CREATE TABLE xml_table (
|
||||
id NUMBER,
|
||||
xml_data SYS.XMLTYPE
|
||||
);
|
||||
|
||||
Inserting into the table can be done by simply binding a string as shown:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
xml_data = """<?xml version="1.0"?>
|
||||
<customer>
|
||||
<name>John Smith</name>
|
||||
<Age>43</Age>
|
||||
<Designation>Professor</Designation>
|
||||
<Subject>Mathematics</Subject>
|
||||
</customer>"""
|
||||
cursor.execute("insert into xml_table values (:id, :xml)",
|
||||
id=1, xml=xml_data)
|
||||
|
||||
This approach works with XML strings up to 1 GB in size. For longer strings, a
|
||||
temporary CLOB must be created using :meth:`Connection.createlob()` and bound
|
||||
as shown:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
clob = connection.createlob(cx_Oracle.DB_TYPE_CLOB)
|
||||
clob.write(xml_data)
|
||||
cursor.execute("insert into xml_table values (:id, sys.xmltype(:xml))",
|
||||
id=2, xml=clob)
|
||||
|
||||
Fetching XML data can be done simply for values that are shorter than the
|
||||
length of a VARCHAR2 column, as shown:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
cursor.execute("select xml_data from xml_table where id = :id", id=1)
|
||||
xml_data, = cursor.fetchone()
|
||||
print(xml_data) # will print the string that was originally stored
|
||||
|
||||
For values that exceed the length of a VARCHAR2 column, a CLOB must be returned
|
||||
instead by using the function ``XMLTYPE.GETCLOBVAL()`` as shown:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
cursor.execute("""
|
||||
select xmltype.getclobval(xml_data)
|
||||
from xml_table
|
||||
where id = :id""", id=1)
|
||||
clob, = cursor.fetchone()
|
||||
print(clob.read())
|
||||
|
||||
The LOB that is returned can be streamed or a string can be returned instead of
|
||||
a CLOB. See :ref:`lobdata` for more information about processing LOBs.
|
Binary file not shown.
After Width: | Height: | Size: 7.2 KiB |
|
@ -0,0 +1,255 @@
|
|||
<!DOCTYPE html>
|
||||
<html itemscope="itemscope" itemtype="http://schema.org/WebPage" lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<title>cx_Oracle - Python Interface for Oracle Database</title>
|
||||
<link rel="stylesheet" href="base.css" type="text/css" />
|
||||
<link rel="shortcut icon" type="image/ico" href="favicon.ico" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="oracleHeader">
|
||||
<div class="container">
|
||||
<a class="oracleLogo" href="https://www.oracle.com/">Oracle</a>
|
||||
</div>
|
||||
</div>
|
||||
<header class="header" role="banner">
|
||||
<div class="container">
|
||||
<div class="headerLogoContainer">
|
||||
<img class="headerLogo" alt="cx_Oracle logo" src="logo.png" />
|
||||
</div>
|
||||
<div class="headerContent">
|
||||
<h1 class="headerTitle">Python cx_Oracle</h1>
|
||||
<nav class="headerNav" role="navigation">
|
||||
<ul>
|
||||
<li><a href="https://cx-oracle.readthedocs.io/en/latest/index.html">Documentation</a></li>
|
||||
<li><a href="https://cx-oracle.readthedocs.io/en/latest/user_guide/installation.html" >Installation</a></li>
|
||||
<li><a href="https://cx-oracle.readthedocs.io/en/latest/release_notes.html#releasenotes">Release Notes</a></li>
|
||||
<li><a href="https://github.com/oracle/python-cx_Oracle">Source code</a></li>
|
||||
<li><a href="https://github.com/oracle/python-cx_Oracle/issues">Help</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</header>
|
||||
|
||||
<main class="mainContent" role="main">
|
||||
<div class="container">
|
||||
|
||||
<div class="announcement">
|
||||
|
||||
<p><strong>cx_Oracle has a major new release under a new name and
|
||||
homepage <a href="https://oracle.github.io/python-oracledb/"
|
||||
>python-oracledb</a>.</strong></p>
|
||||
|
||||
<p><strong>Follow the python-oracledb <a
|
||||
href="https://python-oracledb.readthedocs.io/en/latest/user_guide/installation.html"
|
||||
>installation instructions</a> to start using
|
||||
the upgrade today.</strong></p>
|
||||
|
||||
</div> <!-- announcement -->
|
||||
|
||||
<div id="about">
|
||||
<h2>About cx_Oracle</h2>
|
||||
|
||||
<p><strong>cx_Oracle</strong> is a Python extension module that
|
||||
enables access to Oracle Database. It conforms to the Python
|
||||
database API 2.0 <a
|
||||
href="https://peps.python.org/pep-0249/">
|
||||
specification</a> with a considerable number of additions and a
|
||||
couple of exclusions.</p>
|
||||
|
||||
<p>cx_Oracle 8.3 was tested with Python versions 3.6 through 3.10.
|
||||
Older versions of cx_Oracle may be used with previous Python releases.
|
||||
You can use cx_Oracle with Oracle 11.2, 12, 18, 19 and 21 client
|
||||
libraries. Oracle's standard client-server version interoperability
|
||||
allows connection to both older and newer databases. For example Oracle
|
||||
19c client libraries can connect to Oracle Database 11.2.</p>
|
||||
|
||||
</div> <!-- /about -->
|
||||
|
||||
<div id="quickstart">
|
||||
<h2>Getting Started</h2>
|
||||
|
||||
See <a
|
||||
href="https://cx-oracle.readthedocs.io/en/latest/user_guide/installation.html#quick-start-cx-oracle-installation"
|
||||
>Quick Start cx_Oracle Installation</a> and the <a
|
||||
href="samples/tutorial/Python-and-Oracle-Database-Scripting-for-the-Future.html">Python
|
||||
and Oracle Database Tutorial: Scripting for the Future</a>.
|
||||
|
||||
</div> <!-- /quickstart -->
|
||||
|
||||
<div id="installation">
|
||||
<h2>Installation</h2>
|
||||
|
||||
See <a
|
||||
href="https://cx-oracle.readthedocs.io/en/latest/user_guide/installation.html"
|
||||
>cx_Oracle Installation</a> for detailed instructions.
|
||||
|
||||
</div> <!-- /installation -->
|
||||
|
||||
<div id="documentation">
|
||||
<h2>Documentation</h2>
|
||||
|
||||
See the <a href="https://cx-oracle.readthedocs.io" >cx_Oracle
|
||||
Documentation</a> and <a
|
||||
href="https://cx-oracle.readthedocs.io/en/latest/release_notes.html#releasenotes"
|
||||
>Release Notes</a>.
|
||||
|
||||
</div> <!-- /documentation -->
|
||||
|
||||
<div id="example">
|
||||
<h2>Samples</h2>
|
||||
|
||||
See the <a
|
||||
href="https://github.com/oracle/python-cx_Oracle/tree/main/samples"
|
||||
>/samples</a> directory. You can also look at the scripts in <a
|
||||
href="https://github.com/anthony-tuininga/cx_OracleTools">cx_OracleTools</a>
|
||||
and the modules in <a
|
||||
href="https://github.com/anthony-tuininga/cx_PyOracleLib">cx_PyOracleLib</a>.
|
||||
|
||||
</div> <!-- /example -->
|
||||
|
||||
<div id="help">
|
||||
<h2>Help</h2>
|
||||
|
||||
<p>Issues and questions can be raised with the cx_Oracle community on
|
||||
<a href="https://github.com/oracle/python-cx_Oracle/issues"
|
||||
>GitHub</a> or on the <a
|
||||
href="https://sourceforge.net/projects/cx-oracle/lists/cx-oracle-users"
|
||||
>mailing list</a>.</p>
|
||||
|
||||
</div> <!-- /help -->
|
||||
|
||||
<div id="tests">
|
||||
<h2>Tests</h2>
|
||||
|
||||
See the <a href="https://github.com/oracle/python-cx_Oracle/tree/main/test" >test suite</a>.
|
||||
|
||||
</div> <!-- /tests -->
|
||||
|
||||
<div id="contribuing">
|
||||
<h2>Contributing</h2>
|
||||
|
||||
See <a href="https://github.com/oracle/python-cx_Oracle/blob/main/CONTRIBUTING.md" >CONTRIBUTING</a>.
|
||||
|
||||
</div> <!-- /contributing -->
|
||||
|
||||
<div id="features">
|
||||
|
||||
<h2>Features</h2>
|
||||
|
||||
<ul>
|
||||
<li><p>Easily installed from PyPI.</p></li>
|
||||
|
||||
<li><p>Support for Python 3.6 and higher. Older versions of cx_Oracle may
|
||||
be used with previous Python releases.</p></li>
|
||||
|
||||
<li><p>Support for Oracle Client 11.2, 12, 18, 19 and 21.
|
||||
Oracle's standard cross-version interoperability, allows easy upgrades
|
||||
and connectivity to different Oracle Database versions.</p></li>
|
||||
|
||||
<li><p>Connect to Oracle Database 9.2, 10, 11, 12, 18, 19 or 21
|
||||
(depending on the Oracle Client version used).</p></li>
|
||||
|
||||
<li><p>SQL and PL/SQL Execution. The underlying Oracle Client
|
||||
libraries have significant optimizations including compressed fetch,
|
||||
pre-fetching, client and server result set caching, and statement
|
||||
caching with auto-tuning.</p></li>
|
||||
|
||||
<li><p>Full use of Oracle Network Service infrastructure, including
|
||||
encrypted network traffic and security features.</p></li>
|
||||
|
||||
<li><p>Extensive Oracle data type support, including large object
|
||||
support (CLOB and BLOB).</p></li>
|
||||
|
||||
<li><p>Direct binding to SQL objects. One great use case is binding
|
||||
Python objects to Oracle Spatial SDO objects.</p></li>
|
||||
|
||||
<li><p>JSON datatype support.</p></li>
|
||||
|
||||
<li><p>SODA (Simple Oracle Document Access).</p></li>
|
||||
|
||||
<li><p>Array operations for efficient INSERT and UPDATEs.</p></li>
|
||||
|
||||
<li><p>Array row counts and batch error handling for array operations.</p></li>
|
||||
|
||||
<li><p>Fetching of large result sets.</p></li>
|
||||
|
||||
<li><p>REF CURSOR support.</p></li>
|
||||
|
||||
<li><p>Support for scrollable cursors. Go back and forth through your
|
||||
query results.</p></li>
|
||||
|
||||
<li><p>Fetch PL/SQL Implicit Results. Easily return query results from
|
||||
PL/SQL.</p></li>
|
||||
|
||||
<li><p>Row Prefetching. Efficient use of the network.</p></li>
|
||||
|
||||
<li><p>Client Result Caching. Improve performance of frequently
|
||||
executed look-up statements.</p></li>
|
||||
|
||||
<li><p>Support for Advanced Queuing. Use database notifications to
|
||||
build micro-service applications.</p></li>
|
||||
|
||||
<li><p>Continuous Query Notification (CQN). Get notified when data
|
||||
changes.</p></li>
|
||||
|
||||
<li><p>Support for Edition Based Redefinition. Easily switch
|
||||
applications to use updated PL/SQL logic.</p></li>
|
||||
|
||||
<li><p>Support for setting application context during the creation of
|
||||
a connection, making application metadata more accessible to the
|
||||
database, including in LOGON triggers.</p></li>
|
||||
|
||||
<li><p>End-to-end monitoring and tracing.</p></li>
|
||||
|
||||
<li><p>Transaction Management.</p></li>
|
||||
|
||||
<li><p>Session Pooling, with tagging and session state fix-up callback.</p></li>
|
||||
|
||||
<li><p>Database Resident Connection Pooling (DRCP).</p></li>
|
||||
|
||||
<li><p>Privileged Connections.</p></li>
|
||||
|
||||
<li><p>External Authentication.</p></li>
|
||||
|
||||
<li><p>Database startup and shutdown.</p></li>
|
||||
|
||||
<li><p>Sharded Databases.</p></li>
|
||||
|
||||
<li><p>Oracle Database High Availability Features, such as FAN
|
||||
notifications, Application Continuity, and Transaction Guard support.</p></li>
|
||||
|
||||
</ul>
|
||||
|
||||
<p>DB API specification exclusions: The time data type is not
|
||||
supported by Oracle and is therefore not implemented. The method
|
||||
<code>cursor.nextset()</code> is not implemented either as the DB API
|
||||
specification assumes an implementation of cursors that does not fit
|
||||
well with Oracle's implementation of cursors and implicit results. See
|
||||
the method
|
||||
<a href="https://cx-oracle.readthedocs.io/en/latest/api_manual/cursor.html#Cursor.getimplicitresults">cursor.getimplicitresults()</a>
|
||||
for more information.
|
||||
</p>
|
||||
|
||||
</div> <!-- /features -->
|
||||
|
||||
<div id="license">
|
||||
<h2>License</h2>
|
||||
|
||||
<p>cx_Oracle is licensed under a BSD license which you can find <a
|
||||
href="https://github.com/oracle/python-cx_Oracle/blob/main/LICENSE.txt"
|
||||
>here</a>. The cx_Oracle project is open source and maintained by Oracle Corp.</p>
|
||||
|
||||
</div> <!-- /license -->
|
||||
|
||||
</div>
|
||||
</main>
|
||||
</body>
|
||||
|
||||
</html>
|
1
odpi
1
odpi
|
@ -1 +0,0 @@
|
|||
Subproject commit f73a7c13d643b3fe252614bafc930afbd8e287dd
|
|
@ -1,3 +0,0 @@
|
|||
[build-system]
|
||||
requires = ["setuptools >= 40.6.0", "wheel"]
|
||||
build-backend = "setuptools.build_meta"
|
|
@ -1,59 +0,0 @@
|
|||
# Samples
|
||||
|
||||
## News
|
||||
|
||||
**cx_Oracle has a major new release under a new name and homepage
|
||||
[python-oracledb](https://oracle.github.io/python-oracledb/).**
|
||||
|
||||
**New projects should install python-oracledb instead of cx_Oracle.**
|
||||
|
||||
**The new source code and samples can be found at
|
||||
[github.com/oracle/python-oracledb](https://github.com/oracle/python-oracledb).**
|
||||
|
||||
## cx_Oracle Examples
|
||||
|
||||
This directory contains samples for [cx_Oracle][6]. Documentation is
|
||||
[here][7]. A separate tutorial is [here][8].
|
||||
|
||||
1. The schemas and SQL objects that are referenced in the samples can be
|
||||
created by running the Python script [setup_samples.py][1]. The script
|
||||
requires SYSDBA privileges and will prompt for these credentials as well as
|
||||
the names of the schemas and edition that will be created, unless a number
|
||||
of environment variables are set as documented in the Python script
|
||||
[sample_env.py][2]. Run the script using the following command:
|
||||
|
||||
python setup_samples.py
|
||||
|
||||
Alternatively, the [SQL script][3] can be run directly via SQL\*Plus, which
|
||||
will always prompt for the names of the schemas and edition that will be
|
||||
created.
|
||||
|
||||
sqlplus sys/syspassword@hostname/servicename @sql/setup_samples.sql
|
||||
|
||||
2. Run a Python script, for example:
|
||||
|
||||
python query.py
|
||||
|
||||
3. After running cx_Oracle samples, the schemas and SQL objects can be
|
||||
dropped by running the Python script [drop_samples.py][4]. The script
|
||||
requires SYSDBA privileges and will prompt for these credentials as well as
|
||||
the names of the schemas and edition that will be dropped, unless a number
|
||||
of environment variables are set as documented in the Python script
|
||||
[sample_env.py][2]. Run the script using the following command:
|
||||
|
||||
python drop_samples.py
|
||||
|
||||
Alternatively, the [SQL script][5] can be run directly via SQL\*Plus, which
|
||||
will always prompt for the names of the schemas and edition that will be
|
||||
dropped.
|
||||
|
||||
sqlplus sys/syspassword@hostname/servicename @sql/drop_samples.sql
|
||||
|
||||
[1]: https://github.com/oracle/python-cx_Oracle/blob/main/samples/setup_samples.py
|
||||
[2]: https://github.com/oracle/python-cx_Oracle/blob/main/samples/sample_env.py
|
||||
[3]: https://github.com/oracle/python-cx_Oracle/blob/main/samples/sql/setup_samples.sql
|
||||
[4]: https://github.com/oracle/python-cx_Oracle/blob/main/samples/drop_samples.py
|
||||
[5]: https://github.com/oracle/python-cx_Oracle/blob/main/samples/sql/drop_samples.sql
|
||||
[6]: https://oracle.github.io/python-cx_Oracle/
|
||||
[7]: http://cx-oracle.readthedocs.org/en/latest/index.html
|
||||
[8]: https://oracle.github.io/python-cx_Oracle/samples/tutorial/Python-and-Oracle-Database-Scripting-for-the-Future.html
|
|
@ -1,36 +0,0 @@
|
|||
#------------------------------------------------------------------------------
|
||||
# Copyright (c) 2016, 2021, 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.
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
# app_context.py
|
||||
# This script demonstrates the use of application context. Application
|
||||
# context is available within logon triggers and can be retrieved by using the
|
||||
# function sys_context().
|
||||
#
|
||||
# This script requires cx_Oracle 5.3 and higher.
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
import cx_Oracle as oracledb
|
||||
import sample_env
|
||||
|
||||
# define constants used throughout the script; adjust as desired
|
||||
APP_CTX_NAMESPACE = "CLIENTCONTEXT"
|
||||
APP_CTX_ENTRIES = [
|
||||
( APP_CTX_NAMESPACE, "ATTR1", "VALUE1" ),
|
||||
( APP_CTX_NAMESPACE, "ATTR2", "VALUE2" ),
|
||||
( APP_CTX_NAMESPACE, "ATTR3", "VALUE3" )
|
||||
]
|
||||
|
||||
connection = oracledb.connect(sample_env.get_main_connect_string(),
|
||||
appcontext=APP_CTX_ENTRIES)
|
||||
cursor = connection.cursor()
|
||||
for namespace, name, value in APP_CTX_ENTRIES:
|
||||
cursor.execute("select sys_context(:1, :2) from dual", (namespace, name))
|
||||
value, = cursor.fetchone()
|
||||
print("Value of context key", name, "is", value)
|
|
@ -1,46 +0,0 @@
|
|||
#------------------------------------------------------------------------------
|
||||
# Copyright (c) 2018, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
# aq_notification.py
|
||||
# This script demonstrates using advanced queuing notification. Once this
|
||||
# script is running, use another session to enqueue a few messages to the
|
||||
# "DEMO_BOOK_QUEUE" queue. This is most easily accomplished by running the
|
||||
# object_aq.py sample.
|
||||
#
|
||||
# This script requires cx_Oracle 6.4 and higher.
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
import time
|
||||
|
||||
import cx_Oracle as oracledb
|
||||
import sample_env
|
||||
|
||||
registered = True
|
||||
|
||||
def process_messages(message):
|
||||
global registered
|
||||
print("Message type:", message.type)
|
||||
if message.type == oracledb.EVENT_DEREG:
|
||||
print("Deregistration has taken place...")
|
||||
registered = False
|
||||
return
|
||||
print("Queue name:", message.queueName)
|
||||
print("Consumer name:", message.consumerName)
|
||||
|
||||
connection = oracledb.connect(sample_env.get_main_connect_string(),
|
||||
events=True)
|
||||
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)
|
||||
print("--> Namespace:", sub.namespace)
|
||||
print("--> Protocol:", sub.protocol)
|
||||
print("--> Timeout:", sub.timeout)
|
||||
|
||||
while registered:
|
||||
print("Waiting for notifications....")
|
||||
time.sleep(5)
|
|
@ -1,48 +0,0 @@
|
|||
#------------------------------------------------------------------------------
|
||||
# Copyright (c) 2016, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
# array_dml_rowcounts.py
|
||||
#
|
||||
# Demonstrate 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.
|
||||
#
|
||||
# This script requires cx_Oracle 5.2 and higher.
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
import cx_Oracle as oracledb
|
||||
import sample_env
|
||||
|
||||
connection = oracledb.connect(sample_env.get_main_connect_string())
|
||||
cursor = connection.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"""):
|
||||
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()
|
||||
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.")
|
|
@ -1,86 +0,0 @@
|
|||
#------------------------------------------------------------------------------
|
||||
# Copyright (c) 2016, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
# batch_errors.py
|
||||
#
|
||||
# Demonstrate 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.
|
||||
#
|
||||
# This script requires cx_Oracle 5.2 and higher.
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
import cx_Oracle as oracledb
|
||||
import sample_env
|
||||
|
||||
connection = oracledb.connect(sample_env.get_main_connect_string())
|
||||
cursor = connection.cursor()
|
||||
|
||||
# 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'),
|
||||
]
|
||||
|
||||
# retrieve the number of rows in the table
|
||||
cursor.execute("""
|
||||
select count(*)
|
||||
from ChildTable""")
|
||||
count, = cursor.fetchone()
|
||||
print("number of rows in child table:", int(count))
|
||||
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:
|
||||
cursor.executemany("insert into ChildTable values (:1, :2, :3)",
|
||||
data_to_insert)
|
||||
except oracledb.DatabaseError as e:
|
||||
error, = e.args
|
||||
print("FAILED with error:", error.message)
|
||||
print("number of rows which succeeded:", cursor.rowcount)
|
||||
|
||||
# demonstrate that the row count is accurate
|
||||
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
|
||||
connection.rollback()
|
||||
|
||||
# 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)
|
||||
|
||||
# where errors have taken place, the row count is 0; otherwise it is 1
|
||||
row_counts = cursor.getarraydmlrowcounts()
|
||||
print("Array DML row counts:", row_counts)
|
||||
|
||||
# display the errors that have taken place
|
||||
errors = cursor.getbatcherrors()
|
||||
print("number of errors which took place:", len(errors))
|
||||
for error in errors:
|
||||
print("Error", error.message.rstrip(), "at row offset", error.offset)
|
||||
|
||||
# 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 successful insert:", int(count))
|
|
@ -1,75 +0,0 @@
|
|||
#------------------------------------------------------------------------------
|
||||
# Copyright (c) 2016, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
# bind_insert.py
|
||||
#
|
||||
# Demonstrate how to insert a row into a table using bind variables.
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
import cx_Oracle as oracledb
|
||||
import sample_env
|
||||
|
||||
connection = oracledb.connect(sample_env.get_main_connect_string())
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
# "Bind by position"
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
rows = [
|
||||
(1, "First"),
|
||||
(2, "Second"),
|
||||
(3, "Third"),
|
||||
(4, "Fourth"),
|
||||
(5, None), # Insert a NULL value
|
||||
(6, "Sixth"),
|
||||
(7, "Seventh")
|
||||
]
|
||||
|
||||
cursor = connection.cursor()
|
||||
|
||||
# predefine maximum string size to avoid data scans and memory reallocations;
|
||||
# the None value indicates that the default processing can take place
|
||||
cursor.setinputsizes(None, 20)
|
||||
|
||||
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}
|
||||
]
|
||||
|
||||
cursor = connection.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",)
|
||||
]
|
||||
|
||||
cursor = connection.cursor()
|
||||
cursor.executemany("insert into mytab(id, data) values (11, :1)", rows)
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
# Now query the results back
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
# Don't commit - this lets the demo be run multiple times
|
||||
#connection.commit()
|
||||
|
||||
for row in cursor.execute('select * from mytab'):
|
||||
print(row)
|
|
@ -1,31 +0,0 @@
|
|||
#------------------------------------------------------------------------------
|
||||
# Copyright (c) 2016, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
# bind_query.py
|
||||
#
|
||||
# Demonstrate how to perform a simple query limiting the rows retrieved using
|
||||
# a bind variable. Since the query that is executed is identical, 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 SQL injection attacks.
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
import cx_Oracle as oracledb
|
||||
import sample_env
|
||||
|
||||
connection = oracledb.connect(sample_env.get_main_connect_string())
|
||||
|
||||
cursor = connection.cursor()
|
||||
sql = 'select * from SampleQueryTab where id = :bvid'
|
||||
|
||||
print("Query results with id = 4")
|
||||
for row in cursor.execute(sql, bvid = 4):
|
||||
print(row)
|
||||
print()
|
||||
|
||||
print("Query results with id = 1")
|
||||
for row in cursor.execute(sql, bvid = 1):
|
||||
print(row)
|
||||
print()
|
|
@ -1,75 +0,0 @@
|
|||
#------------------------------------------------------------------------------
|
||||
# Copyright (c) 2019, 2021, 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.
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
# bulk_aq.py
|
||||
# This script 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.
|
||||
#
|
||||
# This script requires cx_Oracle 8.2 and higher.
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
import cx_Oracle as oracledb
|
||||
import sample_env
|
||||
|
||||
QUEUE_NAME = "DEMO_RAW_QUEUE"
|
||||
PAYLOAD_DATA = [
|
||||
"The first message",
|
||||
"The second message",
|
||||
"The third message",
|
||||
"The fourth message",
|
||||
"The fifth message",
|
||||
"The sixth message",
|
||||
"The seventh message",
|
||||
"The eighth message",
|
||||
"The ninth message",
|
||||
"The tenth message",
|
||||
"The eleventh message",
|
||||
"The twelfth and final message"
|
||||
]
|
||||
|
||||
# connect to database
|
||||
connection = oracledb.connect(sample_env.get_main_connect_string())
|
||||
cursor = connection.cursor()
|
||||
|
||||
# create queue
|
||||
queue = connection.queue(QUEUE_NAME)
|
||||
queue.deqoptions.wait = oracledb.DEQ_NO_WAIT
|
||||
queue.deqoptions.navigation = oracledb.DEQ_FIRST_MSG
|
||||
|
||||
# dequeue all existing messages to ensure the queue is empty, just so that
|
||||
# the results are consistent
|
||||
while queue.deqone():
|
||||
pass
|
||||
|
||||
# enqueue a few messages
|
||||
print("Enqueuing messages...")
|
||||
batch_size = 6
|
||||
data_to_enqueue = PAYLOAD_DATA
|
||||
while data_to_enqueue:
|
||||
batch_data = data_to_enqueue[:batch_size]
|
||||
data_to_enqueue = data_to_enqueue[batch_size:]
|
||||
messages = [connection.msgproperties(payload=d) for d in batch_data]
|
||||
for data in batch_data:
|
||||
print(data)
|
||||
queue.enqmany(messages)
|
||||
connection.commit()
|
||||
|
||||
# dequeue the messages
|
||||
print("\nDequeuing messages...")
|
||||
batch_size = 8
|
||||
while True:
|
||||
messages = queue.deqmany(batch_size)
|
||||
if not messages:
|
||||
break
|
||||
for props in messages:
|
||||
print(props.payload.decode())
|
||||
connection.commit()
|
||||
print("\nDone.")
|
|
@ -1,41 +0,0 @@
|
|||
#------------------------------------------------------------------------------
|
||||
# Copyright (c) 2019, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
# call_timeout.py
|
||||
#
|
||||
# Demonstrate the use of the Oracle Client 18c 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.
|
||||
#
|
||||
# This script requires cx_Oracle 7.0 and higher and Oracle Client 18.1 and
|
||||
# higher.
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
import cx_Oracle as oracledb
|
||||
import sample_env
|
||||
|
||||
connection = oracledb.connect(sample_env.get_main_connect_string())
|
||||
connection.call_timeout = 2000
|
||||
print("Call timeout set at", connection.call_timeout, "milliseconds...")
|
||||
|
||||
cursor = connection.cursor()
|
||||
cursor.execute("select sysdate from dual")
|
||||
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"
|
||||
|
||||
print("Sleeping...should time out...")
|
||||
try:
|
||||
cursor.callproc(sleep_proc_name, (3,))
|
||||
except oracledb.DatabaseError as e:
|
||||
print("ERROR:", e)
|
||||
|
||||
cursor.execute("select sysdate from dual")
|
||||
today, = cursor.fetchone()
|
||||
print("Fetch of current date after timeout:", today)
|
|
@ -1,78 +0,0 @@
|
|||
#------------------------------------------------------------------------------
|
||||
# Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
# connection_pool.py
|
||||
# This script demonstrates the use of connection pooling. Pools can
|
||||
# significantly reduce connection times for long running applications that
|
||||
# repeatedly open and close connections. Internal features help protect against
|
||||
# dead connections, and also aid use of Oracle Database features such as FAN
|
||||
# and Application Continuity.
|
||||
# The script uses threading to show multiple users of the pool. One thread
|
||||
# performs a database sleep while another performs a query. A more typical
|
||||
# application might be a web service that handles requests from multiple users.
|
||||
# Note only one operation (such as an execute or fetch) can take place at a time
|
||||
# on each connection.
|
||||
#
|
||||
# Also see session_callback.py.
|
||||
#
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
import threading
|
||||
|
||||
import cx_Oracle as oracledb
|
||||
import sample_env
|
||||
|
||||
# Create a Connection Pool
|
||||
pool = oracledb.SessionPool(user=sample_env.get_main_user(),
|
||||
password=sample_env.get_main_password(),
|
||||
dsn=sample_env.get_connect_string(), min=2, max=5,
|
||||
increment=1)
|
||||
|
||||
def the_long_query():
|
||||
with pool.acquire() as conn:
|
||||
cursor = conn.cursor()
|
||||
cursor.arraysize = 25000
|
||||
print("the_long_query(): beginning execute...")
|
||||
cursor.execute("""
|
||||
select *
|
||||
from
|
||||
TestNumbers
|
||||
cross join TestNumbers
|
||||
cross join TestNumbers
|
||||
cross join TestNumbers
|
||||
cross join TestNumbers
|
||||
cross join TestNumbers""")
|
||||
print("the_long_query(): done execute...")
|
||||
while True:
|
||||
rows = cursor.fetchmany()
|
||||
if not rows:
|
||||
break
|
||||
print("the_long_query(): fetched", len(rows), "rows...")
|
||||
print("the_long_query(): all done!")
|
||||
|
||||
|
||||
def do_a_lock():
|
||||
with pool.acquire() as conn:
|
||||
# dbms_session.sleep() replaces dbms_lock.sleep()
|
||||
# from Oracle Database 18c
|
||||
sleep_proc_name = "dbms_session.sleep" \
|
||||
if int(conn.version.split(".")[0]) >= 18 \
|
||||
else "dbms_lock.sleep"
|
||||
cursor = conn.cursor()
|
||||
print("do_a_lock(): beginning execute...")
|
||||
cursor.callproc(sleep_proc_name, (5,))
|
||||
print("do_a_lock(): done execute...")
|
||||
|
||||
|
||||
thread1 = threading.Thread(target=the_long_query)
|
||||
thread1.start()
|
||||
|
||||
thread2 = threading.Thread(target=do_a_lock)
|
||||
thread2.start()
|
||||
|
||||
thread1.join()
|
||||
thread2.join()
|
||||
|
||||
print("All done!")
|
|
@ -1,68 +0,0 @@
|
|||
#------------------------------------------------------------------------------
|
||||
# Copyright (c) 2016, 2021, 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.
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
# cqn.py
|
||||
# This script 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.
|
||||
#
|
||||
# This script requires cx_Oracle 5.3 and higher.
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
import time
|
||||
|
||||
import cx_Oracle as oracledb
|
||||
import sample_env
|
||||
|
||||
registered = True
|
||||
|
||||
def callback(message):
|
||||
global registered
|
||||
print("Message type:", message.type)
|
||||
if not message.registered:
|
||||
print("Deregistration has taken place...")
|
||||
registered = False
|
||||
return
|
||||
print("Message database name:", message.dbname)
|
||||
print("Message tranasction id:", message.txid)
|
||||
print("Message queries:")
|
||||
for query in message.queries:
|
||||
print("--> Query ID:", query.id)
|
||||
print("--> Query Operation:", query.operation)
|
||||
for table in query.tables:
|
||||
print("--> --> Table Name:", table.name)
|
||||
print("--> --> Table Operation:", table.operation)
|
||||
if table.rows is not None:
|
||||
print("--> --> Table Rows:")
|
||||
for row in table.rows:
|
||||
print("--> --> --> Row RowId:", row.rowid)
|
||||
print("--> --> --> Row Operation:", row.operation)
|
||||
print("-" * 60)
|
||||
print("=" * 60)
|
||||
|
||||
connection = oracledb.connect(sample_env.get_main_connect_string(),
|
||||
events=True)
|
||||
qos = oracledb.SUBSCR_QOS_QUERY | oracledb.SUBSCR_QOS_ROWIDS
|
||||
sub = connection.subscribe(callback=callback, timeout=1800, qos=qos)
|
||||
print("Subscription:", sub)
|
||||
print("--> Connection:", sub.connection)
|
||||
print("--> Callback:", sub.callback)
|
||||
print("--> Namespace:", sub.namespace)
|
||||
print("--> Protocol:", sub.protocol)
|
||||
print("--> Timeout:", sub.timeout)
|
||||
print("--> Operations:", sub.operations)
|
||||
print("--> Rowids?:", bool(sub.qos & oracledb.SUBSCR_QOS_ROWIDS))
|
||||
query_id = sub.registerquery("select * from TestTempTable")
|
||||
print("Registered query:", query_id)
|
||||
|
||||
while registered:
|
||||
print("Waiting for notifications....")
|
||||
time.sleep(5)
|
|
@ -1,74 +0,0 @@
|
|||
#------------------------------------------------------------------------------
|
||||
# Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
# cqn2.py
|
||||
# This script 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.
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
# This script requires cx_Oracle 7 or higher.
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
import time
|
||||
|
||||
import cx_Oracle as oracledb
|
||||
import sample_env
|
||||
|
||||
registered = True
|
||||
|
||||
def callback(message):
|
||||
global registered
|
||||
if not message.registered:
|
||||
print("Deregistration has taken place...")
|
||||
registered = False
|
||||
return
|
||||
connection = pool.acquire()
|
||||
for query in message.queries:
|
||||
for table in query.tables:
|
||||
if table.rows is None:
|
||||
print("Too many row changes detected in table", table.name)
|
||||
continue
|
||||
num_rows_deleted = 0
|
||||
print(len(table.rows), "row changes detected in table", table.name)
|
||||
for row in table.rows:
|
||||
if row.operation & oracledb.OPCODE_DELETE:
|
||||
num_rows_deleted += 1
|
||||
continue
|
||||
ops = []
|
||||
if row.operation & oracledb.OPCODE_INSERT:
|
||||
ops.append("inserted")
|
||||
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()
|
||||
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.SessionPool(user=sample_env.get_main_user(),
|
||||
password=sample_env.get_main_password(),
|
||||
dsn=sample_env.get_connect_string(), min=2, max=5,
|
||||
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)
|
||||
print("Subscription created with ID:", sub.id)
|
||||
query_id = sub.registerquery("select * from TestTempTable")
|
||||
print("Registered query with ID:", query_id)
|
||||
|
||||
while registered:
|
||||
print("Waiting for notifications....")
|
||||
time.sleep(5)
|
|
@ -1,65 +0,0 @@
|
|||
#------------------------------------------------------------------------------
|
||||
# Copyright (c) 2016, 2021, 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.
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
# database_change_notification.py
|
||||
# This script 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.
|
||||
#
|
||||
# This script requires cx_Oracle 5.3 and higher.
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
import time
|
||||
|
||||
import cx_Oracle as oracledb
|
||||
import sample_env
|
||||
|
||||
registered = True
|
||||
|
||||
def callback(message):
|
||||
global registered
|
||||
print("Message type:", message.type)
|
||||
if not message.registered:
|
||||
print("Deregistration has taken place...")
|
||||
registered = False
|
||||
return
|
||||
print("Message database name:", message.dbname)
|
||||
print("Message tranasction id:", message.txid)
|
||||
print("Message tables:")
|
||||
for table in message.tables:
|
||||
print("--> Table Name:", table.name)
|
||||
print("--> Table Operation:", table.operation)
|
||||
if table.rows is not None:
|
||||
print("--> Table Rows:")
|
||||
for row in table.rows:
|
||||
print("--> --> Row RowId:", row.rowid)
|
||||
print("--> --> Row Operation:", row.operation)
|
||||
print("-" * 60)
|
||||
print("=" * 60)
|
||||
|
||||
connection = oracledb.connect(sample_env.get_main_connect_string(),
|
||||
events=True)
|
||||
sub = connection.subscribe(callback=callback, timeout=1800,
|
||||
qos=oracledb.SUBSCR_QOS_ROWIDS)
|
||||
print("Subscription:", sub)
|
||||
print("--> Connection:", sub.connection)
|
||||
print("--> ID:", sub.id)
|
||||
print("--> Callback:", sub.callback)
|
||||
print("--> Namespace:", sub.namespace)
|
||||
print("--> Protocol:", sub.protocol)
|
||||
print("--> Timeout:", sub.timeout)
|
||||
print("--> Operations:", sub.operations)
|
||||
print("--> Rowids?:", bool(sub.qos & oracledb.SUBSCR_QOS_ROWIDS))
|
||||
sub.registerquery("select * from TestTempTable")
|
||||
|
||||
while registered:
|
||||
print("Waiting for notifications....")
|
||||
time.sleep(5)
|
|
@ -1,34 +0,0 @@
|
|||
#------------------------------------------------------------------------------
|
||||
# Copyright (c) 2016, 2021, 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.
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
# database_shutdown.py
|
||||
# This script demonstrates shutting down a database using Python. The
|
||||
# connection used assumes that the environment variable ORACLE_SID has been
|
||||
# set.
|
||||
#
|
||||
# This script requires cx_Oracle 4.3 and higher.
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
import cx_Oracle as oracledb
|
||||
|
||||
# need to connect as SYSDBA or SYSOPER
|
||||
connection = oracledb.connect(mode=oracledb.SYSDBA)
|
||||
|
||||
# first shutdown() call must specify the mode, if DBSHUTDOWN_ABORT is used,
|
||||
# there is no need for any of the other steps
|
||||
connection.shutdown(mode=oracledb.DBSHUTDOWN_IMMEDIATE)
|
||||
|
||||
# now close and dismount the database
|
||||
cursor = connection.cursor()
|
||||
cursor.execute("alter database close normal")
|
||||
cursor.execute("alter database dismount")
|
||||
|
||||
# perform the final shutdown call
|
||||
connection.shutdown(mode=oracledb.DBSHUTDOWN_FINAL)
|
|
@ -1,29 +0,0 @@
|
|||
#------------------------------------------------------------------------------
|
||||
# Copyright (c) 2016, 2021, 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.
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
# database_startup.py
|
||||
# This script demonstrates starting up a database using Python. The
|
||||
# connection used assumes that the environment variable ORACLE_SID has been
|
||||
# set.
|
||||
#
|
||||
# This script requires cx_Oracle 4.3 and higher.
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
import cx_Oracle as oracledb
|
||||
|
||||
# the connection must be in PRELIM_AUTH mode
|
||||
connection = oracledb.connect(mode=oracledb.SYSDBA | oracledb.PRELIM_AUTH)
|
||||
connection.startup()
|
||||
|
||||
# the following statements must be issued in normal SYSDBA mode
|
||||
connection = oracledb.connect("/", mode=oracledb.SYSDBA)
|
||||
cursor = connection.cursor()
|
||||
cursor.execute("alter database mount")
|
||||
cursor.execute("alter database open")
|
|
@ -1,44 +0,0 @@
|
|||
#------------------------------------------------------------------------------
|
||||
# Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
# dbms_output.py
|
||||
# This script demonstrates one method of fetching the lines produced by
|
||||
# the DBMS_OUTPUT package.
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
import cx_Oracle as oracledb
|
||||
import sample_env
|
||||
|
||||
connection = oracledb.connect(sample_env.get_main_connect_string())
|
||||
cursor = connection.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 the cx_Oracle manual');
|
||||
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:
|
||||
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
|
|
@ -1,44 +0,0 @@
|
|||
#------------------------------------------------------------------------------
|
||||
# Copyright (c) 2017, 2021, 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.
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
# dml_returning_multiple_rows.py
|
||||
# This script demonstrates the use of DML returning with multiple rows being
|
||||
# returned at once.
|
||||
#
|
||||
# This script requires cx_Oracle 6.0 and higher.
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
import cx_Oracle as oracledb
|
||||
import sample_env
|
||||
|
||||
# truncate table first so that script can be rerun
|
||||
connection = oracledb.connect(sample_env.get_main_connect_string())
|
||||
cursor = connection.cursor()
|
||||
print("Truncating table...")
|
||||
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)
|
||||
cursor.execute("insert into TestTempTable values (:1, :2)", data)
|
||||
|
||||
# now delete them and use DML returning to return the data that was inserted
|
||||
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)
|
||||
print("Data returned:")
|
||||
for int_val, string_val in zip(int_col.getvalue(), string_col.getvalue()):
|
||||
print(tuple([int_val, string_val]))
|
|
@ -1,43 +0,0 @@
|
|||
#------------------------------------------------------------------------------
|
||||
# Copyright (c) 2016, 2021, 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.
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
# drcp.py
|
||||
# This script demonstrates the use of Database Resident Connection Pooling
|
||||
# (DRCP) which provides a connection pool in the database server, thereby
|
||||
# reducing the cost of creating and tearing down client connections. The pool
|
||||
# can be started and stopped in the database by issuing the following commands
|
||||
# in SQL*Plus:
|
||||
#
|
||||
# exec dbms_connection_pool.start_pool()
|
||||
# exec dbms_connection_pool.stop_pool()
|
||||
#
|
||||
# Statistics regarding the pool can be acquired from the following query:
|
||||
#
|
||||
# select * from v$cpool_cc_stats;
|
||||
#
|
||||
# There is no difference in how a connection is used once it has been
|
||||
# established.
|
||||
#
|
||||
# DRCP has most benefit when used in conjunction with cx_Oracle's local
|
||||
# connection pool, see the cx_Oracle documentation.
|
||||
#
|
||||
# This script requires cx_Oracle 5.0 or higher.
|
||||
#
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
import cx_Oracle as oracledb
|
||||
import sample_env
|
||||
|
||||
conn = oracledb.connect(sample_env.get_drcp_connect_string(),
|
||||
cclass="PYCLASS", purity=oracledb.ATTR_PURITY_SELF)
|
||||
cursor = conn.cursor()
|
||||
print("Performing query using DRCP...")
|
||||
for row in cursor.execute("select * from TestNumbers order by IntCol"):
|
||||
print(row)
|
|
@ -1,24 +0,0 @@
|
|||
#------------------------------------------------------------------------------
|
||||
# Copyright (c) 2019, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
# drop_samples.py
|
||||
#
|
||||
# Drops the database objects used for the cx_Oracle samples.
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
import cx_Oracle as oracledb
|
||||
import sample_env
|
||||
|
||||
def drop_samples(conn):
|
||||
print("Dropping sample schemas and edition...")
|
||||
sample_env.run_sql_script(conn, "drop_samples",
|
||||
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 = oracledb.connect(sample_env.get_admin_connect_string())
|
||||
drop_samples(conn)
|
||||
print("Done.")
|
|
@ -1,76 +0,0 @@
|
|||
#------------------------------------------------------------------------------
|
||||
# Copyright (c) 2016, 2021, 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.
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
# editioning.py
|
||||
# This script demonstrates the use of Edition-Based Redefinition, available
|
||||
# in Oracle# Database 11.2 and higher. See the Oracle documentation on the
|
||||
# subject for additional information. Adjust the contants at the top of the
|
||||
# script for your own database as needed.
|
||||
#
|
||||
# This script requires cx_Oracle 5.3 and higher.
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
import os
|
||||
|
||||
import cx_Oracle as oracledb
|
||||
import sample_env
|
||||
|
||||
# connect to the editions user and create a procedure
|
||||
edition_connect_string = sample_env.get_edition_connect_string()
|
||||
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;""")
|
||||
result = cursor.callfunc("TestEditions", str)
|
||||
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;""")
|
||||
result = cursor.callfunc("TestEditions", str)
|
||||
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))
|
||||
|
||||
# the edition can be set upon connection
|
||||
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))
|
||||
|
||||
# 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))
|
||||
cursor = connection.cursor()
|
||||
result = cursor.callfunc("TestEditions", str)
|
||||
print("Function should return 'Edition 1 Procedure', actually returns:",
|
||||
repr(result))
|
|
@ -1,47 +0,0 @@
|
|||
#------------------------------------------------------------------------------
|
||||
# Copyright (c) 2016, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
# generic_row_factory.py
|
||||
#
|
||||
# Demonstrate the ability to return named tuples for all queries using a
|
||||
# subclassed cursor and row factory.
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
import collections
|
||||
|
||||
import cx_Oracle as oracledb
|
||||
import sample_env
|
||||
|
||||
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)
|
||||
result = super().execute(statement, args or [])
|
||||
if prepare_needed:
|
||||
description = self.description
|
||||
if description is not None:
|
||||
names = [d[0] for d in description]
|
||||
self.rowfactory = collections.namedtuple("GenericQuery", names)
|
||||
return result
|
||||
|
||||
|
||||
# create new subclassed connection and cursor
|
||||
connection = Connection(sample_env.get_main_connect_string())
|
||||
cursor = connection.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)
|
||||
print()
|
||||
|
||||
for row in cursor.execute("select ChildId, Description from ChildTable"):
|
||||
print(row.CHILDID, "->", row.DESCRIPTION)
|
||||
print()
|
|
@ -1,49 +0,0 @@
|
|||
#------------------------------------------------------------------------------
|
||||
# Copyright (c) 2016, 2021, 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.
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
# implicit_results.py
|
||||
# This script demonstrates the use of the 12.1 feature that allows PL/SQL
|
||||
# procedures to return result sets implicitly, without having to explicitly
|
||||
# define them.
|
||||
#
|
||||
# This script requires cx_Oracle 5.3 and higher.
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
import cx_Oracle as oracledb
|
||||
import sample_env
|
||||
|
||||
connection = oracledb.connect(sample_env.get_main_connect_string())
|
||||
cursor = connection.cursor()
|
||||
|
||||
# use PL/SQL block to return two cursors
|
||||
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))
|
||||
for row in result_set:
|
||||
print(row)
|
||||
print()
|
|
@ -1,55 +0,0 @@
|
|||
#------------------------------------------------------------------------------
|
||||
# Copyright (c) 2016, 2021, 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.
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
# insert_geometry.py
|
||||
# This script demonstrates the ability to create Oracle objects (this example
|
||||
# uses SDO_GEOMETRY) and insert them into a table.
|
||||
#
|
||||
# This script requires cx_Oracle 5.3 and higher.
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
import cx_Oracle as oracledb
|
||||
import sample_env
|
||||
|
||||
# create and populate Oracle objects
|
||||
connection = oracledb.connect(sample_env.get_main_connect_string())
|
||||
type_obj = 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")
|
||||
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)
|
||||
|
||||
# create table, if necessary
|
||||
cursor = connection.cursor()
|
||||
cursor.execute("""
|
||||
select count(*)
|
||||
from user_tables
|
||||
where table_name = 'TESTGEOMETRY'""")
|
||||
count, = cursor.fetchone()
|
||||
if count == 0:
|
||||
print("Creating table...")
|
||||
cursor.execute("""
|
||||
create table TestGeometry (
|
||||
IntCol number(9) not null,
|
||||
Geometry MDSYS.SDO_GEOMETRY not null
|
||||
)""")
|
||||
|
||||
# remove all existing rows and then add a new one
|
||||
print("Removing any existing rows...")
|
||||
cursor.execute("delete from TestGeometry")
|
||||
print("Adding row to table...")
|
||||
cursor.execute("insert into TestGeometry values (1, :obj)", obj=obj)
|
||||
connection.commit()
|
||||
print("Success!")
|
|
@ -1,89 +0,0 @@
|
|||
#------------------------------------------------------------------------------
|
||||
# Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
# json_blob.py
|
||||
# Shows how to use a BLOB as a JSON column store.
|
||||
#
|
||||
# Note: with Oracle Database 21c using the new JSON type is recommended
|
||||
# instead, see json_direct.py
|
||||
#
|
||||
# Documentation:
|
||||
# cx_Oracle: https://cx-oracle.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
|
||||
|
||||
import cx_Oracle as oracledb
|
||||
import sample_env
|
||||
|
||||
connection = oracledb.connect(sample_env.get_main_connect_string())
|
||||
|
||||
client_version = oracledb.clientversion()[0]
|
||||
db_version = int(connection.version.split(".")[0])
|
||||
|
||||
# Minimum database vesion is 12
|
||||
if db_version < 12:
|
||||
sys.exit("This example requires Oracle Database 12.1.0.2 or later")
|
||||
|
||||
# Create a table
|
||||
|
||||
cursor = connection.cursor()
|
||||
cursor.execute("""
|
||||
begin
|
||||
execute immediate 'drop table customers';
|
||||
exception when others then
|
||||
if sqlcode <> -942 then
|
||||
raise;
|
||||
end if;
|
||||
end;""")
|
||||
cursor.execute("""
|
||||
create table customers (
|
||||
id integer not null primary key,
|
||||
json_data blob check (json_data is json)
|
||||
) lob (json_data) store as (cache)""")
|
||||
|
||||
# Insert JSON data
|
||||
|
||||
data = dict(name="Rod", dept="Sales", location="Germany")
|
||||
inssql = "insert into customers values (:1, :2)"
|
||||
if client_version >= 21 and db_version >= 21:
|
||||
# Take advantage of direct binding
|
||||
cursor.setinputsizes(None, oracledb.DB_TYPE_JSON)
|
||||
cursor.execute(inssql, [1, data])
|
||||
else:
|
||||
# Insert the data as a JSON string
|
||||
cursor.execute(inssql, [1, json.dumps(data)])
|
||||
|
||||
# Select JSON data
|
||||
|
||||
sql = "SELECT c.json_data FROM customers 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 customers
|
||||
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 customers 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)
|
|
@ -1,94 +0,0 @@
|
|||
#------------------------------------------------------------------------------
|
||||
# Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
# json_direct.py
|
||||
# Shows some JSON features of Oracle Database 21c.
|
||||
# See https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=ADJSN
|
||||
#
|
||||
# For JSON with older databases see json_blob.py
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
import json
|
||||
import sys
|
||||
|
||||
import cx_Oracle as oracledb
|
||||
import sample_env
|
||||
|
||||
connection = oracledb.connect(sample_env.get_main_connect_string())
|
||||
|
||||
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")
|
||||
|
||||
# Create a table
|
||||
|
||||
cursor = connection.cursor()
|
||||
cursor.execute("""
|
||||
begin
|
||||
execute immediate 'drop table customers';
|
||||
exception when others then
|
||||
if sqlcode <> -942 then
|
||||
raise;
|
||||
end if;
|
||||
end;""")
|
||||
cursor.execute("""
|
||||
create table customers (
|
||||
id integer not null primary key,
|
||||
json_data json
|
||||
)""")
|
||||
|
||||
# Insert JSON data
|
||||
|
||||
data = dict(name="Rod", dept="Sales", location="Germany")
|
||||
inssql = "insert into customers values (:1, :2)"
|
||||
if client_version >= 21:
|
||||
# Take advantage of direct binding
|
||||
cursor.setinputsizes(None, oracledb.DB_TYPE_JSON)
|
||||
cursor.execute(inssql, [1, data])
|
||||
else:
|
||||
# Insert the data as a JSON string
|
||||
cursor.execute(inssql, [1, json.dumps(data)])
|
||||
|
||||
# Select JSON data
|
||||
|
||||
sql = "SELECT c.json_data FROM customers c"
|
||||
if client_version >= 21:
|
||||
for j, in cursor.execute(sql):
|
||||
print(j)
|
||||
else:
|
||||
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 customers
|
||||
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 column
|
||||
|
||||
sql = """SELECT c.json_data.location
|
||||
FROM customers c
|
||||
OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY"""
|
||||
if client_version >= 21:
|
||||
for j, in cursor.execute(sql):
|
||||
print(j)
|
||||
else:
|
||||
for j, in cursor.execute(sql):
|
||||
print(json.loads(j.read()))
|
||||
|
||||
# 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)
|
|
@ -1,56 +0,0 @@
|
|||
#------------------------------------------------------------------------------
|
||||
# Copyright (c) 2019, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
# last_rowid.py
|
||||
# Demonstrates the use of the cursor.lastrowid attribute.
|
||||
#
|
||||
# This script requires cx_Oracle 7.3 and higher.
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
import cx_Oracle as oracledb
|
||||
import sample_env
|
||||
|
||||
connection = oracledb.connect(sample_env.get_main_connect_string())
|
||||
|
||||
row1 = [1, "First"]
|
||||
row2 = [2, "Second"]
|
||||
|
||||
# insert a couple of rows and retain the rowid of each
|
||||
cursor = connection.cursor()
|
||||
cursor.execute("insert into mytab (id, data) values (:1, :2)", row1)
|
||||
rowid1 = cursor.lastrowid
|
||||
print("Row 1:", row1)
|
||||
print("Rowid 1:", rowid1)
|
||||
print()
|
||||
|
||||
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 retained
|
||||
cursor.execute("select id, data from mytab where rowid = :1", [rowid1])
|
||||
print("Row 1:", cursor.fetchone())
|
||||
cursor.execute("select id, data from mytab where rowid = :1", [rowid2])
|
||||
print("Row 2:", cursor.fetchone())
|
||||
print()
|
||||
|
||||
# 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])
|
||||
print("Last updated row:", cursor.fetchone())
|
||||
|
||||
# deleting multiple rows only returns the rowid of the last deleted row
|
||||
cursor.execute("delete from mytab")
|
||||
print("Rowid of last deleted row:", cursor.lastrowid)
|
||||
|
||||
# deleting no rows results in a value of None
|
||||
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
|
||||
#connection.commit()
|
|
@ -1,67 +0,0 @@
|
|||
#------------------------------------------------------------------------------
|
||||
# Copyright (c) 2020, 2021, 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.
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
# multi_consumer_aq.py
|
||||
# This script demonstrates how to use multi-consumer advanced queuing. It
|
||||
# makes use of a RAW queue created in the sample setup.
|
||||
#
|
||||
# This script requires cx_Oracle 8.2 and higher.
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
import cx_Oracle as oracledb
|
||||
import sample_env
|
||||
|
||||
QUEUE_NAME = "DEMO_RAW_QUEUE_MULTI"
|
||||
PAYLOAD_DATA = [
|
||||
"The first message",
|
||||
"The second message",
|
||||
"The third message",
|
||||
"The fourth and final message"
|
||||
]
|
||||
|
||||
# connect to database
|
||||
connection = oracledb.connect(sample_env.get_main_connect_string())
|
||||
cursor = connection.cursor()
|
||||
|
||||
# create queue
|
||||
queue = connection.queue(QUEUE_NAME)
|
||||
queue.deqoptions.wait = oracledb.DEQ_NO_WAIT
|
||||
queue.deqoptions.navigation = oracledb.DEQ_FIRST_MSG
|
||||
|
||||
# enqueue a few messages
|
||||
print("Enqueuing messages...")
|
||||
for data in PAYLOAD_DATA:
|
||||
print(data)
|
||||
queue.enqone(connection.msgproperties(payload=data))
|
||||
connection.commit()
|
||||
print()
|
||||
|
||||
# dequeue the messages for consumer A
|
||||
print("Dequeuing the messages for consumer A...")
|
||||
queue.deqoptions.consumername = "SUBSCRIBER_A"
|
||||
while True:
|
||||
props = queue.deqone()
|
||||
if not props:
|
||||
break
|
||||
print(props.payload.decode())
|
||||
connection.commit()
|
||||
print()
|
||||
|
||||
# dequeue the message for consumer B
|
||||
print("Dequeuing the messages for consumer B...")
|
||||
queue.deqoptions.consumername = "SUBSCRIBER_B"
|
||||
while True:
|
||||
props = queue.deqone()
|
||||
if not props:
|
||||
break
|
||||
print(props.payload.decode())
|
||||
connection.commit()
|
||||
|
||||
print("\nDone.")
|
|
@ -1,66 +0,0 @@
|
|||
#------------------------------------------------------------------------------
|
||||
# Copyright (c) 2016, 2021, 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.
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
# object_aq.py
|
||||
# This script demonstrates how to use advanced queuing with objects. It makes
|
||||
# use of a simple type and queue created in the sample setup.
|
||||
#
|
||||
# This script requires cx_Oracle 8.2 and higher.
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
import decimal
|
||||
|
||||
import cx_Oracle as oracledb
|
||||
import sample_env
|
||||
|
||||
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"))
|
||||
]
|
||||
|
||||
# connect to database
|
||||
connection = oracledb.connect(sample_env.get_main_connect_string())
|
||||
cursor = connection.cursor()
|
||||
|
||||
# create queue
|
||||
books_type = connection.gettype(BOOK_TYPE_NAME)
|
||||
queue = connection.queue(QUEUE_NAME, payload_type=books_type)
|
||||
queue.deqoptions.wait = oracledb.DEQ_NO_WAIT
|
||||
queue.deqoptions.navigation = oracledb.DEQ_FIRST_MSG
|
||||
|
||||
# dequeue all existing messages to ensure the queue is empty, just so that
|
||||
# the results are consistent
|
||||
while queue.deqone():
|
||||
pass
|
||||
|
||||
# enqueue a few messages
|
||||
print("Enqueuing messages...")
|
||||
for title, authors, price in BOOK_DATA:
|
||||
book = books_type.newobject()
|
||||
book.TITLE = title
|
||||
book.AUTHORS = authors
|
||||
book.PRICE = price
|
||||
print(title)
|
||||
queue.enqone(connection.msgproperties(payload=book))
|
||||
connection.commit()
|
||||
|
||||
# dequeue the messages
|
||||
print("\nDequeuing messages...")
|
||||
while True:
|
||||
props = queue.deqone()
|
||||
if not props:
|
||||
break
|
||||
print(props.payload.TITLE)
|
||||
connection.commit()
|
||||
print("\nDone.")
|
|
@ -1,46 +0,0 @@
|
|||
#------------------------------------------------------------------------------
|
||||
# Copyright (c) 2016, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
# plsql_collection.py
|
||||
#
|
||||
# Demonstrate how to get the value of a PL/SQL collection from a stored
|
||||
# procedure.
|
||||
#
|
||||
# This feature is new in cx_Oracle 5.3 and is only available in Oracle
|
||||
# Database 12.1 and higher. The ability to get the collection as a dictionary
|
||||
# is new in cx_Oracle 7.0.
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
import cx_Oracle as oracledb
|
||||
import sample_env
|
||||
|
||||
connection = oracledb.connect(sample_env.get_main_connect_string())
|
||||
|
||||
# create new empty object of the correct type
|
||||
# note the use of a PL/SQL type defined in a package
|
||||
type_obj = connection.gettype("PKG_DEMO.UDT_STRINGLIST")
|
||||
obj = type_obj.newobject()
|
||||
|
||||
# call the stored procedure which will populate the object
|
||||
cursor = connection.cursor()
|
||||
cursor.callproc("pkg_Demo.DemoCollectionOut", (obj,))
|
||||
|
||||
# show the indexes that are used by the collection
|
||||
print("Indexes and values of collection:")
|
||||
ix = obj.first()
|
||||
while ix is not None:
|
||||
print(ix, "->", obj.getelement(ix))
|
||||
ix = obj.next(ix)
|
||||
print()
|
||||
|
||||
# show the values as a simple list
|
||||
print("Values of collection as list:")
|
||||
print(obj.aslist())
|
||||
print()
|
||||
|
||||
# show the values as a simple dictionary
|
||||
print("Values of collection as dictionary:")
|
||||
print(obj.asdict())
|
||||
print()
|
|
@ -1,18 +0,0 @@
|
|||
#------------------------------------------------------------------------------
|
||||
# Copyright (c) 2016, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
# plsql_function.py
|
||||
#
|
||||
# Demonstrate how to call a PL/SQL function and get its return value.
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
import cx_Oracle as oracledb
|
||||
import sample_env
|
||||
|
||||
connection = oracledb.connect(sample_env.get_main_connect_string())
|
||||
|
||||
cursor = connection.cursor()
|
||||
res = cursor.callfunc('myfunc', int, ('abc', 2))
|
||||
print(res)
|
|
@ -1,20 +0,0 @@
|
|||
#------------------------------------------------------------------------------
|
||||
# Copyright (c) 2016, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
# plsql_procedure.py
|
||||
#
|
||||
# Demonstrate how to call a PL/SQL stored procedure and get the results of an
|
||||
# OUT variable.
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
import cx_Oracle as oracledb
|
||||
import sample_env
|
||||
|
||||
connection = oracledb.connect(sample_env.get_main_connect_string())
|
||||
|
||||
cursor = connection.cursor()
|
||||
myvar = cursor.var(int)
|
||||
cursor.callproc('myproc', (123, myvar))
|
||||
print(myvar.getvalue())
|
|
@ -1,46 +0,0 @@
|
|||
#------------------------------------------------------------------------------
|
||||
# Copyright (c) 2016, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
# plsql_record.py
|
||||
#
|
||||
# Demonstrate how to bind (in and out) a PL/SQL record.
|
||||
#
|
||||
# This feature is only available in Oracle Database 12.1 and higher.
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
import datetime
|
||||
|
||||
import cx_Oracle as oracledb
|
||||
import sample_env
|
||||
|
||||
connection = oracledb.connect(sample_env.get_main_connect_string())
|
||||
|
||||
# create new object of the correct type
|
||||
# note the use of a PL/SQL record defined in a package
|
||||
# a table record identified by TABLE%ROWTYPE can also be used
|
||||
type_obj = connection.gettype("PKG_DEMO.UDT_DEMORECORD")
|
||||
obj = type_obj.newobject()
|
||||
obj.NUMBERVALUE = 6
|
||||
obj.STRINGVALUE = "Test String"
|
||||
obj.DATEVALUE = datetime.datetime(2016, 5, 28)
|
||||
obj.BOOLEANVALUE = False
|
||||
|
||||
# show the original values
|
||||
print("NUMBERVALUE ->", obj.NUMBERVALUE)
|
||||
print("STRINGVALUE ->", obj.STRINGVALUE)
|
||||
print("DATEVALUE ->", obj.DATEVALUE)
|
||||
print("BOOLEANVALUE ->", obj.BOOLEANVALUE)
|
||||
print()
|
||||
|
||||
# call the stored procedure which will modify the object
|
||||
cursor = connection.cursor()
|
||||
cursor.callproc("pkg_Demo.DemoRecordsInOut", (obj,))
|
||||
|
||||
# show the modified values
|
||||
print("NUMBERVALUE ->", obj.NUMBERVALUE)
|
||||
print("STRINGVALUE ->", obj.STRINGVALUE)
|
||||
print("DATEVALUE ->", obj.DATEVALUE)
|
||||
print("BOOLEANVALUE ->", obj.BOOLEANVALUE)
|
||||
print()
|
|
@ -1,46 +0,0 @@
|
|||
#------------------------------------------------------------------------------
|
||||
# Copyright (c) 2016, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
# query.py
|
||||
#
|
||||
# Demonstrate how to perform a query in different ways.
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
import cx_Oracle as oracledb
|
||||
import sample_env
|
||||
|
||||
connection = oracledb.connect(sample_env.get_main_connect_string())
|
||||
|
||||
sql = """
|
||||
select * from SampleQueryTab
|
||||
where id < 6
|
||||
order by id"""
|
||||
|
||||
print("Get all rows via iterator")
|
||||
cursor = connection.cursor()
|
||||
for result in cursor.execute(sql):
|
||||
print(result)
|
||||
print()
|
||||
|
||||
print("Query one row at a time")
|
||||
cursor.execute(sql)
|
||||
row = cursor.fetchone()
|
||||
print(row)
|
||||
row = cursor.fetchone()
|
||||
print(row)
|
||||
print()
|
||||
|
||||
print("Fetch many rows")
|
||||
cursor.execute(sql)
|
||||
res = cursor.fetchmany(3)
|
||||
print(res)
|
||||
print()
|
||||
|
||||
print("Fetch each row as a Dictionary")
|
||||
cursor.execute(sql)
|
||||
columns = [col[0] for col in cursor.description]
|
||||
cursor.rowfactory = lambda *args: dict(zip(columns, args))
|
||||
for row in cursor:
|
||||
print(row)
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue