diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 63133f72..0af43085 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -13,7 +13,17 @@ 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. +The cx_Oracle driver was renamed to python-oracledb in May 2022. It has a new +repository at https://github.com/oracle/python-oracledb. The installation +instructions are at: +https://python-oracledb.readthedocs.io/en/latest/user_guide/installation.html + +Update to python-oracledb, if possible, and submit your bug report to the +python-oracledb repository. + +No further releases under the cx_Oracle namespace are planned. + +Otherwise, 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 diff --git a/.github/ISSUE_TEMPLATE/documentation-and-example-improvements.md b/.github/ISSUE_TEMPLATE/documentation-and-example-improvements.md index ad77f9cb..cf107847 100644 --- a/.github/ISSUE_TEMPLATE/documentation-and-example-improvements.md +++ b/.github/ISSUE_TEMPLATE/documentation-and-example-improvements.md @@ -9,16 +9,13 @@ assignees: '' - -1. What is the link to the documentation section that needs improving? - -2. Describe the confusion - -3. Suggest changes that would help diff --git a/.github/ISSUE_TEMPLATE/enhancement-requests.md b/.github/ISSUE_TEMPLATE/enhancement-requests.md index 199ecba4..98aaa04c 100644 --- a/.github/ISSUE_TEMPLATE/enhancement-requests.md +++ b/.github/ISSUE_TEMPLATE/enhancement-requests.md @@ -9,16 +9,13 @@ assignees: '' - -1. Describe your new request in detail - -2. Give supporting information about tools and operating systems. Give relevant product version numbers diff --git a/.github/ISSUE_TEMPLATE/general-questions-and-runtime-problems.md b/.github/ISSUE_TEMPLATE/general-questions-and-runtime-problems.md index 78d9d1d9..b73bf700 100644 --- a/.github/ISSUE_TEMPLATE/general-questions-and-runtime-problems.md +++ b/.github/ISSUE_TEMPLATE/general-questions-and-runtime-problems.md @@ -11,7 +11,14 @@ assignees: '' Thank you for using cx_Oracle. -Review the user manual: https://cx-oracle.readthedocs.io/en/latest/index.html +The cx_Oracle driver was renamed to python-oracledb in May 2022. It has a new +repository at https://github.com/oracle/python-oracledb. The installation +instructions are at: +https://python-oracledb.readthedocs.io/en/latest/user_guide/installation.html + +Update to python-oracledb, if possible. + +Otherwise, review the cx_Oracle user manual: https://cx-oracle.readthedocs.io/en/latest/index.html Please answer these questions so we can help you. diff --git a/.github/ISSUE_TEMPLATE/installation-questions.md b/.github/ISSUE_TEMPLATE/installation-questions.md index 0a7b2a8c..be7b94b8 100644 --- a/.github/ISSUE_TEMPLATE/installation-questions.md +++ b/.github/ISSUE_TEMPLATE/installation-questions.md @@ -11,9 +11,16 @@ assignees: '' Thank you for using cx_Oracle. +The cx_Oracle driver was renamed to python-oracledb in May 2022. It has a new +repository at https://github.com/oracle/python-oracledb. The installation +instructions are at: +https://python-oracledb.readthedocs.io/en/latest/user_guide/installation.html + Do these before creating a new issue: - Review and follow the Installation Instructions: https://cx-oracle.readthedocs.io/en/latest/user_guide/installation.html + Update to python-oracledb, if possible. + + Otherwise, 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 diff --git a/.github/SECURITY.md b/.github/SECURITY.md deleted file mode 100644 index 91c3b6f5..00000000 --- a/.github/SECURITY.md +++ /dev/null @@ -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. diff --git a/.github/SUPPORT.md b/.github/SUPPORT.md index 30372283..eeceb44d 100644 --- a/.github/SUPPORT.md +++ b/.github/SUPPORT.md @@ -1,7 +1,9 @@ # Python cx_Oracle Support -cx_Oracle is an Open Source project, so do some searching and reading -before asking questions. +**The cx_Oracle driver was renamed to python-oracledb in May 2022. It has a +new repository at https://github.com/oracle/python-oracledb. Please update to +this new driver. If you still have problems, open an issue on the new +repository.** ## cx_Oracle Installation issues diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 88ff4aa1..9336c8fa 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -1,21 +1,8 @@ Thanks for contributing! -Before submitting PRs for cx_Oracle you must have your signed *Oracle -Contributor Agreement* accepted. See -https://www.oracle.com/technetwork/community/oca-486395.html +The cx_Oracle driver was renamed to python-oracledb in May 2022. It has a new +repository at https://github.com/oracle/python-oracledb. -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. +Please submit your contributions to the python-oracledb repository. -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 -``` - -This can be automatically added to pull requests by committing with: - -``` -git commit --signoff -```` +No further releases under the cx_Oracle namespace are planned. diff --git a/.readthedocs.yaml b/.readthedocs.yaml index ef296026..8ecaf936 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -1,9 +1,16 @@ +# 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 + configuration: doc/src/conf.py +# declare Python requirements required to build docs python: - version: 3.8 - install: - - requirements: doc/requirements.txt + install: + - requirements: doc/requirements.txt diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 5010c7c1..125cf133 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,44 +1,8 @@ # Contributing -We welcome your contributions! There are multiple ways to contribute. +The cx_Oracle driver was renamed to python-oracledb in May 2022. It has a new +repository at https://github.com/oracle/python-oracledb -## Issues +Please submit your contributions to the python-oracledb repository. -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 -``` - -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/) +No further releases under the cx_Oracle namespace are planned. diff --git a/README.md b/README.md index 346711a8..98b0d827 100644 --- a/README.md +++ b/README.md @@ -1,59 +1,33 @@ -# cx_Oracle version 8.3 +# Python cx_Oracle -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 was obsoleted by +[python-oracledb](https://oracle.github.io/python-oracledb/) in 2022.** -cx_Oracle 8.3 has been 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. +Python-oracledb uses the same Python DB API as cx_Oracle, and has many new +features. -## Installation +Install with: -See [cx_Oracle Installation][15]. +``` +python -m pip install oracledb +``` -## Documentation +Usage is like: -See the [cx_Oracle Documentation][2] and [Release Notes][14]. +``` +import getpass +import oracledb -## Samples +un = 'scott' +cs = 'localhost/orclpdb1' +pw = getpass.getpass(f'Enter password for {un}@{cs}: ') -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]. +with oracledb.connect(user=un, password=pw, dsn=cs) as connection: + with connection.cursor() as cursor: + sql = 'select systimestamp from dual' + for r in cursor.execute(sql): + print(r) +``` -## 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://www.python.org/dev/peps/pep-0249 -[2]: http://cx-oracle.readthedocs.io -[3]: https://github.com/oracle/python-cx_Oracle/blob/main/LICENSE.txt -[5]: http://lists.sourceforge.net/lists/listinfo/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 +The source code for python-oracledb is at +[github.com/oracle/python-oracledb](https://github.com/oracle/python-oracledb). diff --git a/README.txt b/README.txt index 46124e25..1313581a 100644 --- a/README.txt +++ b/README.txt @@ -1,5 +1,7 @@ -Please see the cx_Oracle home page for links to documentation, source, build -and installation instructions: +cx_Oracle was obsoleted by python-oracledb in 2022. -https://oracle.github.io/python-cx_Oracle/index.html +Python-oracledb uses the same Python DB API as cx_Oracle, and has many new +features. +See https://python-oracledb.readthedocs.io/en/latest/index.html for how to +install and use this updated driver. diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 00000000..25e76bf5 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,37 @@ +# 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][1] preferably with a proof of concept. Please review +some additional information on [how to report security vulnerabilities to +Oracle][2]. We encourage people who contact Oracle Security to use email +encryption using [our encryption key][3]. + +We ask that you do not use other channels or contact the project maintainers +directly. + +Non-vulnerability related security issues including ideas for new or improved +security features are welcome on GitHub Issues. + +## Security updates, alerts and bulletins + +Security updates will be released on a regular cadence. Many of our projects +will typically release security fixes in conjunction with the Oracle Critical +Patch Update program. Additional information, including past advisories, is +available on our [security alerts][4] page. + +## 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]: mailto:secalert_us@oracle.com +[2]: https://www.oracle.com/corporate/security-practices/assurance/vulnerability/reporting.html +[3]: https://www.oracle.com/security-alerts/encryptionkey.html +[4]: https://www.oracle.com/security-alerts/ diff --git a/doc/requirements.txt b/doc/requirements.txt index e18fa42a..dcc8e5c8 100644 --- a/doc/requirements.txt +++ b/doc/requirements.txt @@ -1,2 +1,2 @@ -sphinx==4.1.2 -sphinx_rtd_theme==0.5.1 +sphinx>=4.2.0 +sphinx-rtd-theme>=0.5.2 diff --git a/doc/src/api_manual/aq.rst b/doc/src/api_manual/aq.rst index ad9c9e89..8ca7002f 100644 --- a/doc/src/api_manual/aq.rst +++ b/doc/src/api_manual/aq.rst @@ -4,348 +4,7 @@ Advanced Queuing (AQ) ********************* -See :ref:`aqusermanual` for more information about using AQ in cx_Oracle. +.. include:: ../note.rst -.. 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` 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` 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 - ` 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 ` 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` 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 - ` 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 `. 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`. +See `API: Advanced Queueing (AQ) `__ in the python-oracledb documentation. diff --git a/doc/src/api_manual/connection.rst b/doc/src/api_manual/connection.rst index 4694822d..23826b20 100644 --- a/doc/src/api_manual/connection.rst +++ b/doc/src/api_manual/connection.rst @@ -4,749 +4,7 @@ Connection Object ***************** -.. note:: +.. include:: ../note.rst - 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 ` 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 ` 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 ` 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 `__ 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 ` 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 ` 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 ` 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 ` 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 ` 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 ` 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 '' for single consumer queues and - ':' 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). +See `API: Connection Objects `__ in the python-oracledb documentation. diff --git a/doc/src/api_manual/cursor.rst b/doc/src/api_manual/cursor.rst index b89b2f18..1702d89d 100644 --- a/doc/src/api_manual/cursor.rst +++ b/doc/src/api_manual/cursor.rst @@ -4,668 +4,7 @@ Cursor Object ************* +.. include:: ../note.rst -.. 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 ` 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 `. 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 `__ 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 ` - 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 is too large", where 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(num_rows=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 ` 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 ` 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 - `, :ref:`DB API constants `, 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`. - - 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 `__ - 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 ` 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. +See `API: Cursor Objects `__ in the python-oracledb documentation. diff --git a/doc/src/api_manual/deprecations.rst b/doc/src/api_manual/deprecations.rst index e2b7a2f7..0e5fce6e 100644 --- a/doc/src/api_manual/deprecations.rst +++ b/doc/src/api_manual/deprecations.rst @@ -4,174 +4,8 @@ Deprecations ************ -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. +.. include:: ../note.rst -.. 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. +See `Deprecated and Desupported Features `__ in the python-oracledb +documentation. diff --git a/doc/src/api_manual/lob.rst b/doc/src/api_manual/lob.rst index 6ae61f85..8525b4a9 100644 --- a/doc/src/api_manual/lob.rst +++ b/doc/src/api_manual/lob.rst @@ -4,95 +4,7 @@ LOB Objects *********** -See :ref:`lobdata` for more information about using LOBs. +.. include:: ../note.rst -.. 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 `. - - .. 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. +See `API: LOB Objects `__ in the python-oracledb documentation. diff --git a/doc/src/api_manual/module.rst b/doc/src/api_manual/module.rst index dac1a2fe..899078e4 100644 --- a/doc/src/api_manual/module.rst +++ b/doc/src/api_manual/module.rst @@ -6,1570 +6,7 @@ Module Interface **************** -.. data:: __future__ +.. include:: ../note.rst - Special object which contains attributes which control the behavior of - cx_Oracle, allowing for opting in for new features. No attributes are - currently supported so all attributes will silently ignore being set and - will always appear to have the value None. - - .. note:: - - This method is an extension to the DB API definition. - - .. versionadded:: 6.2 - - -.. function:: Binary(string) - - Construct an object holding a binary (long) string value. - - -.. function:: clientversion() - - Return the version of the client library being used as a 5-tuple. The five - values are the major version, minor version, update number, patch number - and port update number. - - .. note:: - - This method is an extension to the DB API definition. - - -.. function:: connect(user=None, password=None, dsn=None, \ - mode=cx_Oracle.DEFAULT_AUTH, handle=0, pool=None, threaded=False, \ - events=False, cclass=None, purity=cx_Oracle.ATTR_PURITY_DEFAULT, \ - newpassword=None, encoding=None, nencoding=None, edition=None, \ - appcontext=[], tag=None, matchanytag=None, shardingkey=[], \ - supershardingkey=[], stmtcachesize=20) - Connection(user=None, password=None, dsn=None, \ - mode=cx_Oracle.DEFAULT_AUTH, handle=0, pool=None, threaded=False, \ - events=False, cclass=None, purity=cx_Oracle.ATTR_PURITY_DEFAULT, \ - newpassword=None, encoding=None, nencoding=None, edition=None, \ - appcontext=[], tag=None, matchanytag=False, shardingkey=[], \ - supershardingkey=[], stmtcachesize=20) - - Constructor for creating a connection to the database. Return a - :ref:`connection object `. All parameters are optional and can be - specified as keyword parameters. See :ref:`connhandling` information about - connections. - - The dsn (data source name) is the TNS entry (from the Oracle names server - or tnsnames.ora file) or is a string like the one returned from - :meth:`~cx_Oracle.makedsn()`. If the user parameter is passed and the - password and dsn parameters are not passed, the user parameter is assumed - to be a connect string in the format ``user/password@dsn``, the same format - accepted by Oracle applications such as SQL\*Plus. See :ref:`connstr` for - more information. - - If the mode is specified, it must be one of the - :ref:`connection authorization modes` - which are defined at the module level. - - If the handle is specified, it must be of type OCISvcCtx\* and is only of - use when embedding Python in an application (like PowerBuilder) which has - already made the connection. The connection thus created should *never* be - used after the source handle has been closed or destroyed. - - The pool parameter is expected to be a - :ref:`session pool object ` and the use of this parameter is the - equivalent of calling :meth:`SessionPool.acquire()`. Parameters not - accepted by that method are ignored. - - The threaded parameter is expected to be a boolean expression which - indicates whether or not Oracle should wrap accesses to connections with a - mutex. - - The events parameter is expected to be a boolean expression which indicates - whether or not to initialize Oracle in events mode. This is required for - continuous query notification and high availability event notifications. - - The cclass parameter is expected to be a string and defines 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 newpassword parameter is expected to be a string if specified and sets - the password for the logon during the connection process. - - See the :ref:`globalization ` section for details on the - encoding and nencoding parameters. Note the default encoding and nencoding - values changed to "UTF-8" in cx_Oracle 8, and any character set in NLS_LANG - is ignored. In a future release of cx_Oracle, only UTF-8 will be supported. - - The edition parameter is expected to be a string if specified and sets the - edition to use for the session. It is only relevant if both the client and - the database are at least Oracle Database 11.2. If this parameter is used - with the cclass parameter the exception "DPI-1058: edition not supported - with connection class" will be raised. - - The appcontext parameter is expected to be a list of 3-tuples, if specified, - and sets the application context for the connection. Application context - is available in the database by using the sys_context() PL/SQL method and - can be used within a logon trigger as well as any other PL/SQL procedures. - Each entry in the list is expected to contain three strings: the namespace, - the name and the value. - - The tag parameter, if specified, is expected to be a string 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 ` 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. - - The stmtcachesize parameter, if specified, is expected to be an integer - which specifies the initial value of :data:`~Connection.stmtcachesize`. - - .. versionchanged:: 8.2 - - The parameter `stmtcachesize` was added. - -.. function:: Cursor(connection) - - Constructor for creating a cursor. Return a new - :ref:`cursor object ` using the connection. - - .. note:: - - This method is an extension to the DB API definition. - - -.. function:: Date(year, month, day) - - Construct an object holding a date value. - - -.. function:: DateFromTicks(ticks) - - Construct an object holding a date value from the given ticks value (number - of seconds since the epoch; see the documentation of the standard Python - time module for details). - - -.. function:: init_oracle_client(lib_dir=None, config_dir=None, \ - error_url=None, driver_name=None) - - Initialize the Oracle client library now, rather than when - :func:`cx_Oracle.clientversion()`, :func:`cx_Oracle.connect()` or - :func:`cx_Oracle.SessionPool()` is called for the first time. If - initialization has already taken place, an exception is raised. - - If the parameter `lib_dir` is not `None` or the empty string, - the specified directory is the only one searched for the Oracle Client - libraries; otherwise, the standard way of locating the Oracle Client - library is used. - - If the parameter `config_dir` is not `None` or the empty string, the - specified directory is used to find Oracle Client library configuration - files. This is equivalent to setting the environment variable `TNS_ADMIN` - and overrides any value already set in `TNS_ADMIN`. If this parameter is not - set, the standard way of locating Oracle Client library configuration files - is used. - - If the parameter `error_url` is not `None` or the empty string, the - specified value is included in the message of the exception raised when the - Oracle Client library cannot be loaded; otherwise, the :ref:`installation` - URL is included. - - If the parameter `driver_name` is not `None` or the empty string, the - specified value can be found in database views that give information about - connections. For example, it is in the ``CLIENT_DRIVER`` column of - ``V$SESSION_CONNECT_INFO``. The standard is to set this value to - ``" : version>"``, where is the name of the driver and - is its version. There should be a single space character before - and after the colon. If this value is not specified, then the default - value of "cx_Oracle : " is used. - - See :ref:`initialization` for more discussion. - - .. note:: - - This method is an extension to the DB API definition. - - -.. function:: makedsn(host, port, sid=None, service_name=None, region=None, \ - sharding_key=None, super_sharding_key=None) - - Return a string suitable for use as the dsn parameter for - :meth:`~cx_Oracle.connect()`. This string is identical to the strings that - are defined by the Oracle names server or defined in the tnsnames.ora file. - - .. note:: - - This method is an extension to the DB API definition. - - -.. function:: SessionPool(user=None, password=None, dsn=None, min=1, max=2, \ - increment=1, connectiontype=cx_Oracle.Connection, threaded=True, \ - getmode=cx_Oracle.SPOOL_ATTRVAL_NOWAIT, events=False, \ - homogeneous=True, externalauth=False, encoding=None, nencoding=None, \ - edition=None, timeout=0, wait_timeout=0, max_lifetime_session=0, \ - session_callback=None, max_sessions_per_shard=0, \ - soda_metadata_cache=False, stmtcachesize=20, ping_interval=60) - - Create and return a :ref:`session pool object `. Session pooling - (also known as connection pooling) creates a pool of available connections - to the database, allowing applications to acquire a connection very quickly. - It is of primary use in a server where connections are requested in rapid - succession and used for a short period of time, for example in a web server. - See :ref:`connpool` for more information. - - Connection pooling in cx_Oracle is handled by Oracle's - `Session pooling `__ - technology. This allows cx_Oracle applications to support features - like `Application Continuity `__. - - The user, password and dsn parameters are the same as for - :meth:`cx_Oracle.connect()` - - The min, max and increment parameters control pool growth behavior. A fixed - pool size where min equals max is recommended to help prevent connection - storms and to help overall system stability. The min parameter is the - number of connections opened when the pool is created. The increment is the - number of connections that are opened whenever a connection request exceeds - the number of currently open connections. The max parameter is the maximum - number of connections that can be open in the connection pool. - - Note that when using :ref:`external authentication `, - :ref:`heterogeneous pools `, or :ref:`drcp`, then the pool - growth behavior is different. In these cases the number of connections - created at pool startup is always zero, and the increment is always one. - - If the connectiontype parameter is specified, all calls to - :meth:`~SessionPool.acquire()` will create connection objects of that type, - rather than the base type defined at the module level. - - The getmode parameter indicates whether or not future - :func:`SessionPool.acquire()` calls will wait for available connections. It - can be one of the :ref:`Session Pool Get Modes ` values. - - The events parameter is expected to be a boolean expression which indicates - whether or not to initialize Oracle in events mode. This is required for - continuous query notification and high availability event notifications. - - The homogeneous parameter is expected to be a boolean expression which - indicates whether or not to create a homogeneous pool. A homogeneous pool - requires that all connections in the pool use the same credentials. As such - proxy authentication and external authentication is not possible with a - homogeneous pool. See :ref:`Heterogeneous and Homogeneous Connection Pools - `. - - The externalauth parameter is expected to be a boolean expression which - indicates whether or not external authentication should be used. External - authentication implies that something other than the database is - authenticating the user to the database. This includes the use of operating - system authentication and Oracle wallets. See :ref:`Connecting Using - External Authentication `. - - The encoding and nencoding parameters set the encodings used for string - values transferred between cx_Oracle and Oracle Database, see - :ref:`Character Sets and Globalization `. Note the default - encoding and nencoding values changed to "UTF-8" in cx_Oracle 8, and any - character set in NLS_LANG is ignored. In a future release of cx_Oracle, - only UTF-8 will be supported. - - The edition parameter is expected to be a string, if specified, and sets the - edition to use for the sessions in the pool. It is only relevant if both the - client and the server are at least Oracle Database 11.2. See - :ref:`Edition-Based Redefinition (EBR) `. - - The timeout parameter is expected to be an integer, if specified, and sets - the length of time (in seconds) after which idle sessions in the pool are - terminated. Note that termination only occurs when the pool is accessed. - The default value of 0 means that no idle sessions are terminated. The - initial pool timeout must be non-zero if you subsequently want to change - the timeout with :meth:`SessionPool.reconfigure()`. - - The wait_timeout parameter is expected to be an integer, if specified, and - sets the length of 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 is set to the value - :data:`cx_Oracle.SPOOL_ATTRVAL_TIMEDWAIT`. - - The max_lifetime_session parameter is expected to be an integer, if - specified, and sets the maximum length of time (in seconds) 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. The default value is 0 - which means that there is no maximum length of time that a pooled session - may exist. - - The session_callback parameter is expected to be either a string or a - callable. If the session_callback parameter is a callable, it will be - called when a newly created connection is returned from the pool, or when a - tag is requested and that tag does not match the connection's actual tag. - The callable will be invoked with the connection and the requested tag as - its only parameters. If the parameter is a string, it should be the name - of a PL/SQL procedure that will be called when - :func:`SessionPool.acquire()` requests a tag and that tag does not match - the connection's actual tag. See :ref:`Session CallBacks for Setting Pooled - Connection State `. Support for the PL/SQL procedure - requires Oracle Client libraries 12.2 or later. See the `OCI documentation - `__ for more information. - - The max_sessions_per_shard parameter is expected to be an integer, if - specified. Setting this 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 value is ignored if the Oracle client library version is less than - 18.3. - - The soda_metadata_cache parameter is expected to be a boolean expresion - which indicates whether or not to enable the SODA metatata cache. This - significantly improves the performance of methods - :meth:`SodaDatabase.createCollection()` and - :meth:`SodaDatabase.openCollection()` but can become out of date as it does - not track changes made externally. It is also only available in Oracle - Client 19.11 and higher. See :ref:`Using the SODA Metadata Cache - `. - - The stmtcachesize parameter, if specified, is expected to be an integer - which specifies the initial value of :data:`~SessionPool.stmtcachesize`. - - The ping_interval parameter, if specified, is expected to be an integer - which specifies the initial value of :data:`~SessionPool.ping_interval`. - - .. note:: - - This method is an extension to the DB API definition. - - .. versionchanged:: 8.2 - - The parameters `soda_metadata_cache`, `stmtcachesize` and - `ping_interval` were added. For consistency and compliance with the PEP - 8 naming style, the parameter `waitTimeout` was renamed to - `wait_timeout`, the parameter `maxLifetimeSession` was renamed to - `max_lifetime_session`, the parameter `sessionCallback` was renamed to - `session_callback` and the parameter `maxSessionsPerShard` was renamed - to `max_sessions_per_shard`. The old names will continue to work as - keyword parameters for a period of time. The `threaded` parameter value - is ignored and threading is always enabled; in previous versions the - default was False. - - -.. function:: Time(hour, minute, second) - - Construct an object holding a time value. - - .. note:: - - The time only data type is not supported by Oracle. Calling this - function will raise a NotSupportedError exception. - - - -.. function:: TimeFromTicks(ticks) - - Construct an object holding a time value from the given ticks value (number - of seconds since the epoch; see the documentation of the standard Python - time module for details). - - .. note:: - - The time only data type is not supported by Oracle. Calling this - function will raise a NotSupportedError exception. - - -.. function:: Timestamp(year, month, day, hour, minute, second) - - Construct an object holding a time stamp value. - - -.. function:: TimestampFromTicks(ticks) - - Construct an object holding a time stamp value from the given ticks value - (number of seconds since the epoch; see the documentation of the standard - Python time module for details). - - - -.. _constants: - -Constants -========= - -General -------- - -.. data:: apilevel - - String constant stating the supported DB API level. Currently '2.0'. - - -.. data:: buildtime - - String constant stating the time when the binary was built. - - .. note:: - - This constant is an extension to the DB API definition. - - -.. data:: paramstyle - - String constant stating the type of parameter marker formatting expected by - the interface. Currently 'named' as in 'where name = :name'. - - -.. data:: threadsafety - - Integer constant stating the level of thread safety that the interface - supports. Currently 2, which means that threads may share the module and - connections, but not cursors. Sharing means that a thread may use a - resource without wrapping it using a mutex semaphore to implement resource - locking. - - Note that in order to make use of multiple threads in a program which - intends to connect and disconnect in different threads, the threaded - parameter to :meth:`connect()` must be `True`. - - -.. data:: version -.. data:: __version__ - - String constant stating the version of the module. Currently '|release|'. - - .. note:: - - This attribute is an extension to the DB API definition. - - -Advanced Queuing: Delivery Modes --------------------------------- - -These constants are extensions to the DB API definition. They are possible -values for the :attr:`~DeqOptions.deliverymode` attribute of the -:ref:`dequeue options object ` passed as the options parameter to -the :meth:`Connection.deq()` method as well as the -:attr:`~EnqOptions.deliverymode` attribute of the -:ref:`enqueue options object ` passed as the options parameter to -the :meth:`Connection.enq()` method. They are also possible values for the -:attr:`~MessageProperties.deliverymode` attribute of the -:ref:`message properties object ` passed as the msgproperties -parameter to the :meth:`Connection.deq()` and :meth:`Connection.enq()` methods. - - -.. data:: MSG_BUFFERED - - This constant is used to specify that enqueue/dequeue operations should - enqueue or dequeue buffered messages. - - -.. data:: MSG_PERSISTENT - - This constant is used to specify that enqueue/dequeue operations should - enqueue or dequeue persistent messages. This is the default value. - - -.. data:: MSG_PERSISTENT_OR_BUFFERED - - This constant is used to specify that dequeue operations should dequeue - either persistent or buffered messages. - - -Advanced Queuing: Dequeue Modes -------------------------------- - -These constants are extensions to the DB API definition. They are possible -values for the :attr:`~DeqOptions.mode` attribute of the -:ref:`dequeue options object `. This object is the options -parameter for the :meth:`Connection.deq()` method. - - -.. data:: DEQ_BROWSE - - This constant is used to specify that dequeue should read the message - without acquiring any lock on the message (equivalent to a select - statement). - - -.. data:: DEQ_LOCKED - - This constant is used to specify that dequeue should read and obtain a - write lock on the message for the duration of the transaction (equivalent - to a select for update statement). - - -.. data:: DEQ_REMOVE - - This constant is used to specify that dequeue should read the message and - update or delete it. This is the default value. - - -.. data:: DEQ_REMOVE_NODATA - - This constant is used to specify that dequeue should confirm receipt of the - message but not deliver the actual message content. - - -Advanced Queuing: Dequeue Navigation Modes ------------------------------------------- - -These constants are extensions to the DB API definition. They are possible -values for the :attr:`~DeqOptions.navigation` attribute of the -:ref:`dequeue options object `. This object is the options -parameter for the :meth:`Connection.deq()` method. - - -.. data:: DEQ_FIRST_MSG - - This constant is used to specify that dequeue should retrieve the first - available message that matches the search criteria. This resets the - position to the beginning of the queue. - - -.. data:: DEQ_NEXT_MSG - - This constant is used to specify that dequeue should retrieve the next - available message that matches the search criteria. If the previous message - belongs to a message group, AQ retrieves the next available message that - matches the search criteria and belongs to the message group. This is the - default. - - -.. data:: DEQ_NEXT_TRANSACTION - - This constant is used to specify that dequeue should skip the remainder of - the transaction group and retrieve the first message of the next - transaction group. This option can only be used if message grouping is - enabled for the current queue. - - -Advanced Queuing: Dequeue Visibility Modes ------------------------------------------- - -These constants are extensions to the DB API definition. They are possible -values for the :attr:`~DeqOptions.visibility` attribute of the -:ref:`dequeue options object `. This object is the options -parameter for the :meth:`Connection.deq()` method. - - -.. data:: DEQ_IMMEDIATE - - This constant is used to specify that dequeue should perform its work as - part of an independent transaction. - - -.. data:: DEQ_ON_COMMIT - - This constant is used to specify that dequeue should be part of the current - transaction. This is the default value. - - -Advanced Queuing: Dequeue Wait Modes ------------------------------------- - -These constants are extensions to the DB API definition. They are possible -values for the :attr:`~DeqOptions.wait` attribute of the -:ref:`dequeue options object `. This object is the options -parameter for the :meth:`Connection.deq()` method. - - -.. data:: DEQ_NO_WAIT - - This constant is used to specify that dequeue not wait for messages to be - available for dequeuing. - - -.. data:: DEQ_WAIT_FOREVER - - This constant is used to specify that dequeue should wait forever for - messages to be available for dequeuing. This is the default value. - - -Advanced Queuing: Enqueue Visibility Modes ------------------------------------------- - -These constants are extensions to the DB API definition. They are possible -values for the :attr:`~EnqOptions.visibility` attribute of the -:ref:`enqueue options object `. This object is the options -parameter for the :meth:`Connection.enq()` method. - - -.. data:: ENQ_IMMEDIATE - - This constant is used to specify that enqueue should perform its work as - part of an independent transaction. - - -.. data:: ENQ_ON_COMMIT - - This constant is used to specify that enqueue should be part of the current - transaction. This is the default value. - - -Advanced Queuing: Message States --------------------------------- - -These constants are extensions to the DB API definition. They are possible -values for the :attr:`~MessageProperties.state` attribute of the -:ref:`message properties object `. This object is the -msgproperties parameter for the :meth:`Connection.deq()` and -:meth:`Connection.enq()` methods. - - -.. data:: MSG_EXPIRED - - This constant is used to specify that the message has been moved to the - exception queue. - - -.. data:: MSG_PROCESSED - - This constant is used to specify that the message has been processed and - has been retained. - - -.. data:: MSG_READY - - This constant is used to specify that the message is ready to be processed. - - -.. data:: MSG_WAITING - - This constant is used to specify that the message delay has not yet been - reached. - - -Advanced Queuing: Other ------------------------ - -These constants are extensions to the DB API definition. They are special -constants used in advanced queuing. - - -.. data:: MSG_NO_DELAY - - This constant is a possible value for the :attr:`~MessageProperties.delay` - attribute of the :ref:`message properties object ` passed - as the msgproperties parameter to the :meth:`Connection.deq()` and - :meth:`Connection.enq()` methods. It specifies that no delay should be - imposed and the message should be immediately available for dequeuing. This - is also the default value. - - -.. data:: MSG_NO_EXPIRATION - - This constant is a possible value for the - :attr:`~MessageProperties.expiration` attribute of the - :ref:`message properties object ` passed as the msgproperties - parameter to the :meth:`Connection.deq()` and :meth:`Connection.enq()` - methods. It specifies that the message never expires. This is also the - default value. - - -.. _connection-authorization-modes: - -Connection Authorization Modes ------------------------------- - -These constants are extensions to the DB API definition. They are possible -values for the mode parameter of the :meth:`connect()` method. - - -.. data:: DEFAULT_AUTH - - This constant is used to specify that default authentication is to take - place. This is the default value if no mode is passed at all. - - .. versionadded:: 7.2 - -.. data:: PRELIM_AUTH - - This constant is used to specify that preliminary authentication is to be - used. This is needed for performing database startup and shutdown. - - -.. data:: SYSASM - - This constant is used to specify that SYSASM access is to be acquired. - - -.. data:: SYSBKP - - This constant is used to specify that SYSBACKUP access is to be acquired. - - -.. data:: SYSDBA - - This constant is used to specify that SYSDBA access is to be acquired. - - -.. data:: SYSDGD - - This constant is used to specify that SYSDG access is to be acquired. - - -.. data:: SYSKMT - - This constant is used to specify that SYSKM access is to be acquired. - - -.. data:: SYSOPER - - This constant is used to specify that SYSOPER access is to be acquired. - - -.. data:: SYSRAC - - This constant is used to specify that SYSRAC access is to be acquired. - - -Database Shutdown Modes ------------------------ - -These constants are extensions to the DB API definition. They are possible -values for the mode parameter of the :meth:`Connection.shutdown()` method. - - -.. data:: DBSHUTDOWN_ABORT - - This constant is used to specify that the caller should not wait for - current processing to complete or for users to disconnect from the - database. This should only be used in unusual circumstances since database - recovery may be necessary upon next startup. - - -.. data:: DBSHUTDOWN_FINAL - - This constant is used to specify that the instance can be truly halted. - This should only be done after the database has been shutdown with one of - the other modes (except abort) and the database has been closed and - dismounted using the appropriate SQL commands. - - -.. data:: DBSHUTDOWN_IMMEDIATE - - This constant is used to specify that all uncommitted transactions should - be rolled back and any connected users should be disconnected. - - -.. data:: DBSHUTDOWN_TRANSACTIONAL - - This constant is used to specify that further connections to the database - should be prohibited and no new transactions should be allowed. It then - waits for all active transactions to complete. - - -.. data:: DBSHUTDOWN_TRANSACTIONAL_LOCAL - - This constant is used to specify that further connections to the database - should be prohibited and no new transactions should be allowed. It then - waits for only local active transactions to complete. - - -Event Types ------------ - -These constants are extensions to the DB API definition. They are possible -values for the :attr:`Message.type` attribute of the messages that are sent -for subscriptions created by the :meth:`Connection.subscribe()` method. - - -.. data:: EVENT_AQ - - This constant is used to specify that one or more messages are available - for dequeuing on the queue specified when the subscription was created. - - -.. data:: EVENT_DEREG - - This constant is used to specify that the subscription has been - deregistered and no further notifications will be sent. - - -.. data:: EVENT_NONE - - This constant is used to specify no information is available about the - event. - - -.. data:: EVENT_OBJCHANGE - - This constant is used to specify that a database change has taken place on - a table registered with the :meth:`Subscription.registerquery()` method. - - -.. data:: EVENT_QUERYCHANGE - - This constant is used to specify that the result set of a query registered - with the :meth:`Subscription.registerquery()` method has been changed. - - -.. data:: EVENT_SHUTDOWN - - This constant is used to specify that the instance is in the process of - being shut down. - - -.. data:: EVENT_SHUTDOWN_ANY - - This constant is used to specify that any instance (when running RAC) is in - the process of being shut down. - - -.. data:: EVENT_STARTUP - - This constant is used to specify that the instance is in the process of - being started up. - - -.. _cqn-operation-codes: - -Operation Codes ---------------- - -These constants are extensions to the DB API definition. They are possible -values for the operations parameter for the :meth:`Connection.subscribe()` -method. One or more of these values can be OR'ed together. These values are -also used by the :attr:`MessageTable.operation` or -:attr:`MessageQuery.operation` attributes of the messages that are sent. - - -.. data:: OPCODE_ALLOPS - - This constant is used to specify that messages should be sent for all - operations. - - -.. data:: OPCODE_ALLROWS - - This constant is used to specify that the table or query has been - completely invalidated. - - -.. data:: OPCODE_ALTER - - This constant is used to specify that messages should be sent when a - registered table has been altered in some fashion by DDL, or that the - message identifies a table that has been altered. - - -.. data:: OPCODE_DELETE - - This constant is used to specify that messages should be sent when data is - deleted, or that the message identifies a row that has been deleted. - - -.. data:: OPCODE_DROP - - This constant is used to specify that messages should be sent when a - registered table has been dropped, or that the message identifies a table - that has been dropped. - - -.. data:: OPCODE_INSERT - - This constant is used to specify that messages should be sent when data is - inserted, or that the message identifies a row that has been inserted. - - -.. data:: OPCODE_UPDATE - - This constant is used to specify that messages should be sent when data is - updated, or that the message identifies a row that has been updated. - -.. _sesspoolmodes: - -Session Pool Get Modes ----------------------- - -These constants are extensions to the DB API definition. They are possible -values for the getmode parameter of the :meth:`SessionPool()` method. - - -.. data:: SPOOL_ATTRVAL_FORCEGET - - This constant is used to specify that a new connection will be returned if - there are no free sessions available in the pool. - - -.. data:: SPOOL_ATTRVAL_NOWAIT - - This constant is used to specify that an exception should be raised if - there are no free sessions available in the pool. This is the default - value. - - -.. data:: SPOOL_ATTRVAL_WAIT - - This constant is used to specify that the caller should wait until a - session is available if there are no free sessions available in the pool. - - -.. data:: SPOOL_ATTRVAL_TIMEDWAIT - - This constant is used to specify that the caller should wait for a period - of time (defined by the wait_timeout parameter) for a session to become - available before returning with an error. - - -Session Pool Purity -------------------- - -These constants are extensions to the DB API definition. They are possible -values for the purity parameter of the :meth:`connect()` method, which is used -in database resident connection pooling (DRCP). - - -.. data:: ATTR_PURITY_DEFAULT - - This constant is used to specify that the purity of the session is the - default value identified by Oracle (see Oracle's documentation for more - information). This is the default value. - - -.. data:: ATTR_PURITY_NEW - - This constant is used to specify that the session acquired from the pool - should be new and not have any prior session state. - - -.. data:: ATTR_PURITY_SELF - - This constant is used to specify that the session acquired from the pool - need not be new and may have prior session state. - - -Subscription Grouping Classes ------------------------------ - -These constants are extensions to the DB API definition. They are possible -values for the groupingClass parameter of the :meth:`Connection.subscribe()` -method. - -.. data:: SUBSCR_GROUPING_CLASS_TIME - - This constant is used to specify that events are to be grouped by the - period of time in which they are received. - - -Subscription Grouping Types ---------------------------- - -These constants are extensions to the DB API definition. They are possible -values for the groupingType parameter of the :meth:`Connection.subscribe()` -method. - -.. data:: SUBSCR_GROUPING_TYPE_SUMMARY - - This constant is used to specify that when events are grouped a summary of - the events should be sent instead of the individual events. This is the - default value. - -.. data:: SUBSCR_GROUPING_TYPE_LAST - - This constant is used to specify that when events are grouped the last - event that makes up the group should be sent instead of the individual - events. - - -.. _subscr-namespaces: - -Subscription Namespaces ------------------------ - -These constants are extensions to the DB API definition. They are possible -values for the namespace parameter of the :meth:`Connection.subscribe()` -method. - -.. data:: SUBSCR_NAMESPACE_AQ - - This constant is used to specify that notifications should be sent when a - queue has messages available to dequeue. - -.. data:: SUBSCR_NAMESPACE_DBCHANGE - - This constant is used to specify that database change notification or query - change notification messages are to be sent. This is the default value. - - -.. _subscr-protocols: - -Subscription Protocols ----------------------- - -These constants are extensions to the DB API definition. They are possible -values for the protocol parameter of the :meth:`Connection.subscribe()` method. - - -.. data:: SUBSCR_PROTO_HTTP - - This constant is used to specify that notifications will be sent to an - HTTP URL when a message is generated. This value is currently not - supported. - - -.. data:: SUBSCR_PROTO_MAIL - - This constant is used to specify that notifications will be sent to an - e-mail address when a message is generated. This value is currently not - supported. - - -.. data:: SUBSCR_PROTO_OCI - - This constant is used to specify that notifications will be sent to the - callback routine identified when the subscription was created. It is the - default value and the only value currently supported. - - -.. data:: SUBSCR_PROTO_SERVER - - This constant is used to specify that notifications will be sent to a - PL/SQL procedure when a message is generated. This value is currently not - supported. - - -.. _subscr-qos: - -Subscription Quality of Service -------------------------------- - -These constants are extensions to the DB API definition. They are possible -values for the qos parameter of the :meth:`Connection.subscribe()` method. One -or more of these values can be OR'ed together. - -.. data:: SUBSCR_QOS_BEST_EFFORT - - This constant is used to specify that best effort filtering for query - result set changes is acceptable. False positive notifications may be - received. This behaviour may be suitable for caching applications. - - -.. data:: SUBSCR_QOS_DEREG_NFY - - This constant is used to specify that the subscription should be - automatically unregistered after the first notification is received. - - -.. data:: SUBSCR_QOS_QUERY - - This constant is used to specify that notifications should be sent if the - result set of the registered query changes. By default no false positive - notifications will be generated. - - -.. data:: SUBSCR_QOS_RELIABLE - - This constant is used to specify that notifications should not be lost in - the event of database failure. - - -.. data:: SUBSCR_QOS_ROWIDS - - This constant is used to specify that the rowids of the inserted, updated - or deleted rows should be included in the message objects that are sent. - - -.. _types: - -DB API Types ------------- - -.. data:: BINARY - - This type object is used to describe columns in a database that contain - binary data. The database types :data:`DB_TYPE_RAW` and - :data:`DB_TYPE_LONG_RAW` will compare equal to this value. If a variable is - created with this type, the database type :data:`DB_TYPE_RAW` will be used. - - -.. data:: DATETIME - - This type object is used to describe columns in a database that are dates. - The database types :data:`DB_TYPE_DATE`, :data:`DB_TYPE_TIMESTAMP`, - :data:`DB_TYPE_TIMESTAMP_LTZ` and :data:`DB_TYPE_TIMESTAMP_TZ` will all - compare equal to this value. If a variable is created with this - type, the database type :data:`DB_TYPE_DATE` will be used. - - -.. data:: NUMBER - - This type object is used to describe columns in a database that are - numbers. The database types :data:`DB_TYPE_BINARY_DOUBLE`, - :data:`DB_TYPE_BINARY_FLOAT`, :data:`DB_TYPE_BINARY_INTEGER` and - :data:`DB_TYPE_NUMBER` will all compare equal to this value. If a variable - is created with this type, the database type :data:`DB_TYPE_NUMBER` will be - used. - - -.. data:: ROWID - - This type object is used to describe the pseudo column "rowid". The - database type :data:`DB_TYPE_ROWID` will compare equal to this value. If a - variable is created with this type, the database type - :data:`DB_TYPE_VARCHAR` will be used. - - -.. data:: STRING - - This type object is used to describe columns in a database that are - strings. The database types :data:`DB_TYPE_CHAR`, :data:`DB_TYPE_LONG`, - :data:`DB_TYPE_NCHAR`, :data:`DB_TYPE_NVARCHAR` and :data:`DB_TYPE_VARCHAR` - will all compare equal to this value. If a variable is created with this - type, the database type :data:`DB_TYPE_VARCHAR` will be used. - - -.. _dbtypes: - -Database Types --------------- - -All of these types are extensions to the DB API definition. They are found in -query and object metadata. They can also be used to specify the database type -when binding data. - -.. data:: DB_TYPE_BFILE - - Describes columns, attributes or array elements in a database that are of - type BFILE. It will compare equal to the DB API type :data:`BINARY`. - - -.. data:: DB_TYPE_BINARY_DOUBLE - - Describes columns, attributes or array elements in a database that are of - type BINARY_DOUBLE. It will compare equal to the DB API type - :data:`NUMBER`. - - -.. data:: DB_TYPE_BINARY_FLOAT - - Describes columns, attributes or array elements in a database that are - of type BINARY_FLOAT. It will compare equal to the DB API type - :data:`NUMBER`. - - -.. data:: DB_TYPE_BINARY_INTEGER - - Describes attributes or array elements in a database that are of type - BINARY_INTEGER. It will compare equal to the DB API type :data:`NUMBER`. - - -.. data:: DB_TYPE_BLOB - - Describes columns, attributes or array elements in a database that are of - type BLOB. It will compare equal to the DB API type :data:`BINARY`. - - -.. data:: DB_TYPE_BOOLEAN - - Describes attributes or array elements in a database that are of type - BOOLEAN. It is only available in Oracle 12.1 and higher and only within - PL/SQL. - - -.. data:: DB_TYPE_CHAR - - Describes columns, attributes or array elements in a database that are of - type CHAR. It will compare equal to the DB API type :data:`STRING`. - - Note that these are fixed length string values and behave differently from - VARCHAR2. - - -.. data:: DB_TYPE_CLOB - - Describes columns, attributes or array elements in a database that are of - type CLOB. It will compare equal to the DB API type :data:`STRING`. - - -.. data:: DB_TYPE_CURSOR - - Describes columns in a database that are of type CURSOR. In PL/SQL these - are knoown as REF CURSOR. - - -.. data:: DB_TYPE_DATE - - Describes columns, attributes or array elements in a database that are of - type DATE. It will compare equal to the DB API type :data:`DATETIME`. - - -.. data:: DB_TYPE_INTERVAL_DS - - Describes columns, attributes or array elements in a database that are of - type INTERVAL DAY TO SECOND. - - -.. data:: DB_TYPE_INTERVAL_YM - - Describes columns, attributes or array elements in a database that are of - type INTERVAL YEAR TO MONTH. This database type is not currently supported - by cx_Oracle. - - -.. data:: DB_TYPE_JSON - - Describes columns in a database that are of type JSON (with Oracle Database - 21 or later). - - .. versionadded:: 8.1 - - -.. data:: DB_TYPE_LONG - - Describes columns, attributes or array elements in a database that are of - type LONG. It will compare equal to the DB API type :data:`STRING`. - - -.. data:: DB_TYPE_LONG_RAW - - Describes columns, attributes or array elements in a database that are of - type LONG RAW. It will compare equal to the DB API type :data:`BINARY`. - - -.. data:: DB_TYPE_NCHAR - - Describes columns, attributes or array elements in a database that are of - type NCHAR. It will compare equal to the DB API type :data:`STRING`. - - Note that these are fixed length string values and behave differently from - NVARCHAR2. - - -.. data:: DB_TYPE_NCLOB - - Describes columns, attributes or array elements in a database that are of - type NCLOB. It will compare equal to the DB API type :data:`STRING`. - - -.. data:: DB_TYPE_NUMBER - - Describes columns, attributes or array elements in a database that are of - type NUMBER. It will compare equal to the DB API type :data:`NUMBER`. - - -.. data:: DB_TYPE_NVARCHAR - - Describes columns, attributes or array elements in a database that are of - type NVARCHAR2. It will compare equal to the DB API type :data:`STRING`. - - -.. data:: DB_TYPE_OBJECT - - Describes columns, attributes or array elements in a database that are an - instance of a named SQL or PL/SQL type. - - -.. data:: DB_TYPE_RAW - - Describes columns, attributes or array elements in a database that are of - type RAW. It will compare equal to the DB API type :data:`BINARY`. - - -.. data:: DB_TYPE_ROWID - - Describes columns, attributes or array elements in a database that are of - type ROWID or UROWID. It will compare equal to the DB API type - :data:`ROWID`. - - -.. data:: DB_TYPE_TIMESTAMP - - Describes columns, attributes or array elements in a database that are of - type TIMESTAMP. It will compare equal to the DB API type :data:`DATETIME`. - - -.. data:: DB_TYPE_TIMESTAMP_LTZ - - Describes columns, attributes or array elements in a database that are of - type TIMESTAMP WITH LOCAL TIME ZONE. It will compare equal to the DB API - type :data:`DATETIME`. - - -.. data:: DB_TYPE_TIMESTAMP_TZ - - Describes columns, attributes or array elements in a database that are of - type TIMESTAMP WITH TIME ZONE. It will compare equal to the DB API type - :data:`DATETIME`. - - -.. data:: DB_TYPE_VARCHAR - - Describes columns, attributes or array elements in a database that are of - type VARCHAR2. It will compare equal to the DB API type :data:`STRING`. - - -.. _dbtypesynonyms: - -Database Type Synonyms ----------------------- - -All of the following constants are deprecated and will be removed in a future -version of cx_Oracle. - -.. data:: BFILE - - A synonym for :data:`DB_TYPE_BFILE`. - - .. deprecated:: 8.0 - - -.. data:: BLOB - - A synonym for :data:`DB_TYPE_BLOB`. - - .. deprecated:: 8.0 - - -.. data:: BOOLEAN - - A synonym for :data:`DB_TYPE_BOOLEAN`. - - .. deprecated:: 8.0 - - -.. data:: CLOB - - A synonym for :data:`DB_TYPE_CLOB`. - - .. deprecated:: 8.0 - -.. data:: CURSOR - - A synonym for :data:`DB_TYPE_CURSOR`. - - .. deprecated:: 8.0 - - -.. data:: FIXED_CHAR - - A synonym for :data:`DB_TYPE_CHAR`. - - .. deprecated:: 8.0 - - -.. data:: FIXED_NCHAR - - A synonym for :data:`DB_TYPE_NCHAR`. - - .. deprecated:: 8.0 - - -.. data:: INTERVAL - - A synonym for :data:`DB_TYPE_INTERVAL_DS`. - - .. deprecated:: 8.0 - - -.. data:: LONG_BINARY - - A synonym for :data:`DB_TYPE_LONG_RAW`. - - .. deprecated:: 8.0 - - -.. data:: LONG_STRING - - A synonym for :data:`DB_TYPE_LONG`. - - .. deprecated:: 8.0 - - -.. data:: NATIVE_FLOAT - - A synonym for :data:`DB_TYPE_BINARY_DOUBLE`. - - .. deprecated:: 8.0 - - -.. data:: NATIVE_INT - - A synonym for :data:`DB_TYPE_BINARY_INTEGER`. - - .. deprecated:: 8.0 - - -.. data:: NCHAR - - A synonym for :data:`DB_TYPE_NVARCHAR`. - - .. deprecated:: 8.0 - - -.. data:: NCLOB - - A synonym for :data:`DB_TYPE_NCLOB`. - - .. deprecated:: 8.0 - - -.. data:: OBJECT - - A synonym for :data:`DB_TYPE_OBJECT`. - - .. deprecated:: 8.0 - - -.. data:: TIMESTAMP - - A synonym for :data:`DB_TYPE_TIMESTAMP`. - - .. deprecated:: 8.0 - - -Other Types ------------ - -All of these types are extensions to the DB API definition. - -.. data:: ApiType - - This type object is the Python type of the database API type constants - :data:`BINARY`, :data:`DATETIME`, :data:`NUMBER`, :data:`ROWID` and - :data:`STRING`. - - -.. data:: DbType - - This type object is the Python type of the - :ref:`database type constants `. - - -.. data:: LOB - - This type object is the Python type of :data:`DB_TYPE_BLOB`, - :data:`DB_TYPE_BFILE`, :data:`DB_TYPE_CLOB` and :data:`DB_TYPE_NCLOB` data - that is returned from cursors. - - -.. _exceptions: - -Exceptions -========== - -.. exception:: Warning - - Exception raised for important warnings and defined by the DB API but not - actually used by cx_Oracle. - - -.. exception:: Error - - Exception that is the base class of all other exceptions defined by - cx_Oracle and is a subclass of the Python StandardError exception (defined - in the module exceptions). - - -.. exception:: InterfaceError - - Exception raised for errors that are related to the database interface - rather than the database itself. It is a subclass of Error. - - -.. exception:: DatabaseError - - Exception raised for errors that are related to the database. It is a - subclass of Error. - - -.. exception:: DataError - - Exception raised for errors that are due to problems with the processed - data. It is a subclass of DatabaseError. - - -.. exception:: OperationalError - - Exception raised for errors that are related to the operation of the - database but are not necessarily under the control of the programmer. It is - a subclass of DatabaseError. - - -.. exception:: IntegrityError - - Exception raised when the relational integrity of the database is affected. - It is a subclass of DatabaseError. - - -.. exception:: InternalError - - Exception raised when the database encounters an internal error. It is a - subclass of DatabaseError. - - -.. exception:: ProgrammingError - - Exception raised for programming errors. It is a subclass of DatabaseError. - - -.. exception:: NotSupportedError - - Exception raised when a method or database API was used which is not - supported by the database. It is a subclass of DatabaseError. - - -.. _exchandling: - -Exception handling -================== - -.. note:: - - PEP 249 (Python Database API Specification v2.0) says the following about - exception values: - - [...] The values of these exceptions are not defined. They should - give the user a fairly good idea of what went wrong, though. [...] - - With cx_Oracle every exception object has exactly one argument in the - ``args`` tuple. This argument is a ``cx_Oracle._Error`` object which has - the following five read-only attributes. - -.. attribute:: _Error.code - - Integer attribute representing the Oracle error number (ORA-XXXXX). - -.. attribute:: _Error.offset - - Integer attribute representing the error offset when applicable. - -.. attribute:: _Error.message - - String attribute representing the Oracle message of the error. This - message is localized by the environment of the Oracle connection. - -.. attribute:: _Error.context - - String attribute representing the context in which the exception was - raised. - -.. attribute:: _Error.isrecoverable - - Boolean attribute representing whether the error is recoverable or not. - This is False in all cases unless both Oracle Database 12.1 (or later) and - Oracle Client 12.1 (or later) are being used. - - .. versionadded:: 5.3 - - -This allows you to use the exceptions for example in the following way: - -:: - - import cx_Oracle - - connection = cx_Oracle.connect("cx_Oracle/dev@localhost/orclpdb1") - cursor = connection.cursor() - - try: - cursor.execute("select 1 / 0 from dual") - except cx_Oracle.DatabaseError as exc: - error, = exc.args - print("Oracle-Error-Code:", error.code) - print("Oracle-Error-Message:", error.message) +See `API: python-oracledb Module `__ in the python-oracledb documentation. diff --git a/doc/src/api_manual/object_type.rst b/doc/src/api_manual/object_type.rst index 81a6d1f1..6e51d291 100644 --- a/doc/src/api_manual/object_type.rst +++ b/doc/src/api_manual/object_type.rst @@ -4,187 +4,7 @@ Object Type Objects ******************* -.. note:: +.. include:: ../note.rst - 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 - ` 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 `. - - .. 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 ` if the variable binds - Oracle objects; otherwise, it will be one of the - :ref:`database type constants `. - - .. versionadded:: 8.0 +See `API: DbObjectType Objects `__ in the python-oracledb documentation. diff --git a/doc/src/api_manual/session_pool.rst b/doc/src/api_manual/session_pool.rst index 5ef82223..41ce8011 100644 --- a/doc/src/api_manual/session_pool.rst +++ b/doc/src/api_manual/session_pool.rst @@ -4,299 +4,8 @@ SessionPool Object ****************** -.. note:: +.. include:: ../note.rst - 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 `. - - 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 ` 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 - ` 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 - ``aquire()`` 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 `. - - .. 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 ` 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 +See `API: ConnectionPool Objects `__ in the python-oracledb +documentation. diff --git a/doc/src/api_manual/soda.rst b/doc/src/api_manual/soda.rst index 1e4baf75..c6e64a7a 100644 --- a/doc/src/api_manual/soda.rst +++ b/doc/src/api_manual/soda.rst @@ -4,696 +4,7 @@ SODA **** -`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. By default, documents are JSON -strings. See the :ref:`user manual ` for examples. +.. include:: ../note.rst -.. _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 - `__ 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 `. 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 - `__ - 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 ` 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 `. 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 - `__ - 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 `__ - 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 ` 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 ` 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 `. - - .. 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 `. 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 - `__ - and `Monitoring Database Operations - `__ - 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 `. - - .. 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 `. 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 - `__ - and `Monitoring Database Operations - `__ - 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 - `__ - 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 - `__ - and `Monitoring Database Operations - `__ - 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 `. - - -.. attribute:: SodaDoc.createdOn - - This read-only attribute returns the creation time of the document in - `ISO 8601 `__ - 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 `__ - 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 `. - - -.. 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 - `__ - 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 ` 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 ` that match the - criteria. - - .. versionadded:: 7.0 - - -.. method:: SodaOperation.getOne() - - Returns a single :ref:`SODA Document object ` 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 - `__ - and `Monitoring Database Operations - `__ - 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 `. 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 `. - 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 +See `API: SODA `__ in the python-oracledb documentation. diff --git a/doc/src/api_manual/subscription.rst b/doc/src/api_manual/subscription.rst index 264d795d..74cf14e8 100644 --- a/doc/src/api_manual/subscription.rst +++ b/doc/src/api_manual/subscription.rst @@ -4,272 +4,7 @@ Subscription Object ******************* -.. note:: +.. include:: ../note.rst - 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. +See `API: Subscription Objects `__ in the python-oracledb documentation. diff --git a/doc/src/api_manual/variable.rst b/doc/src/api_manual/variable.rst index 4d7e5995..fbd5eeba 100644 --- a/doc/src/api_manual/variable.rst +++ b/doc/src/api_manual/variable.rst @@ -4,104 +4,7 @@ Variable Objects **************** -.. note:: +.. include:: ../note.rst - 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 ` if the variable binds - Oracle objects; otherwise, it will be one of the - :ref:`database type constants `. - - .. 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. +See `API: Variable Objects `__ in the python-oracledb documentation. diff --git a/doc/src/conf.py b/doc/src/conf.py index d4ef72c4..cb940ac6 100644 --- a/doc/src/conf.py +++ b/doc/src/conf.py @@ -33,7 +33,7 @@ # 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' +copyright = u'2016, 2025, 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 @@ -131,4 +131,3 @@ # If false, no module index is generated. #latex_use_modindex = True - diff --git a/doc/src/images/cx_Oracle_arch.png b/doc/src/images/cx_Oracle_arch.png deleted file mode 100644 index 6391afef..00000000 Binary files a/doc/src/images/cx_Oracle_arch.png and /dev/null differ diff --git a/doc/src/index.rst b/doc/src/index.rst index 1dfd77a9..d5fde5b7 100644 --- a/doc/src/index.rst +++ b/doc/src/index.rst @@ -1,15 +1,10 @@ Welcome to cx_Oracle's documentation! ===================================== -**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. +.. include:: note.rst -**cx_Oracle** is distributed under an open-source :ref:`license ` -(the BSD license). A detailed description of cx_Oracle changes can be found in -the :ref:`release notes `. +See `python-oracledb documentation `__. Contents: diff --git a/doc/src/license.rst b/doc/src/license.rst index c8d82e4c..ce7ec50d 100644 --- a/doc/src/license.rst +++ b/doc/src/license.rst @@ -10,7 +10,7 @@ License .. centered:: **LICENSE AGREEMENT FOR CX_ORACLE** -Copyright |copy| 2016, 2020, Oracle and/or its affiliates. All rights reserved. +Copyright |copy| 2016, 2025, Oracle and/or its affiliates. All rights reserved. Copyright |copy| 2007-2015, Anthony Tuininga. All rights reserved. @@ -43,4 +43,3 @@ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. Computronix |reg| is a registered trademark of Computronix (Canada) Ltd. - diff --git a/doc/src/note.rst b/doc/src/note.rst new file mode 100644 index 00000000..3b850925 --- /dev/null +++ b/doc/src/note.rst @@ -0,0 +1,16 @@ +.. note:: + + **cx_Oracle has a major new release under a new name and homepage** + `python-oracledb `__. + + **New projects should install python-oracledb instead of the obsolete + cx_Oracle driver.** + +Python-oracledb uses the same Python Database API as cx_Oracle, supports the +feature requirements of frameworks that rely on this API, and has many new +features. + +To upgrade to python-oracledb, see `Upgrading from cx_Oracle 8.3 to +python-oracledb `__. diff --git a/doc/src/release_notes.rst b/doc/src/release_notes.rst index 25e335ab..bf016854 100644 --- a/doc/src/release_notes.rst +++ b/doc/src/release_notes.rst @@ -5,2068 +5,7 @@ cx_Oracle Release Notes ======================= -For any deprecations, see :ref:`Deprecations `. +.. include:: note.rst -Version 8.3 (November 2021) ---------------------------- - -#) Updated embedded ODPI-C to `version 4.3.0 - `__. -#) Added official support for Python 3.10. -#) Support for dequeuing messages from Oracle Transactional Event Queue (TEQ) - queues was restored. -#) Corrected calculation of attribute :data:`MessageProperties.msgid`. Note - that the attribute is now also read only. -#) Binary integer variables now explicitly convert values to integers (since - implicit conversion to integer has become an error in Python 3.10) and - values that are not `int`, `float` or `decimal.Decimal` are explicitly - rejected. -#) Improved samples and test suite. - - -Version 8.2.1 (June 2021) -------------------------- - -#) Updated embedded ODPI-C to `version 4.2.1 - `__. -#) Added support for caching the database version in pooled connections with - Oracle Client 19 and earlier (later Oracle Clients handle this caching - internally). This optimization eliminates a round-trip previously often - required when reusing a pooled connection. -#) Fixed a regression with error messages when creating a connection fails. -#) Fixed crash when using the deprecated parameter name `keywordParameters` - with :meth:`Cursor.callproc()`. -#) Improved documentation and the test suite. - - -Version 8.2 (May 2021) ----------------------- - -#) Updated embedded ODPI-C to `version 4.2.0 - `__. -#) Threaded mode is now always enabled when creating connection pools with - :meth:`cx_Oracle.SessionPool()`. Any `threaded` parameter value is ignored. -#) Added :meth:`SessionPool.reconfigure()` to support pool reconfiguration. - This method provides the ability to change properties such as the size of - existing pools instead of having to restart the application or create a new - pool. -#) Added parameter `max_sessions_per_shard` to :meth:`cx_Oracle.SessionPool()` - to allow configuration of the maximum number of sessions per shard in the - pool. In addition, the attribute - :data:`SessionPool.max_sessions_per_shard` was added in order to permit - making adjustments after the pool has been created. They are usable when - using Oracle Client version 18.3 and higher. -#) Added parameter `stmtcachesize` to :meth:`cx_Oracle.connect()` and - :meth:`cx_Oracle.SessionPool()` in order to permit specifying the size of - the statement cache during the creation of pools and standalone - connections. -#) Added parameter `ping_interval` to :meth:`cx_Oracle.SessionPool()` to - specify the ping interval when acquiring pooled connections. In addition, - the attribute :data:`SessionPool.ping_interval` was added in order to - permit making adjustments after the pool has been created. In previous - cx_Oracle releases a fixed ping interval of 60 seconds was used. -#) Added parameter `soda_metadata_cache` to :meth:`cx_Oracle.SessionPool()` - for :ref:`SODA metadata cache ` support. In addition, - the attribute :data:`SessionPool.soda_metadata_cache` was added in order to - permit making adjustments after the pool has been created. This feature - significantly improves the performance of methods - :meth:`SodaDatabase.createCollection()` (when not specifying a value for - the metadata parameter) and :meth:`SodaDatabase.openCollection()`. Caching - is available when using Oracle Client version 21.3 and higher (or Oracle - Client 19 from 19.11). -#) Added support for supplying hints to SODA operations. A new non-terminal - method :meth:`~SodaOperation.hint()` was added and a `hint` parameter was - added to the methods :meth:`SodaCollection.insertOneAndGet()`, - :meth:`SodaCollection.insertManyAndGet()` and - :meth:`SodaCollection.saveAndGet()`. All of these require Oracle Client - 21.3 or higher (or Oracle Client 19 from 19.11). -#) Added parameter `bypass_decode` to :meth:`Cursor.var()` in order to allow - the `decode` step to be bypassed when converting data from Oracle Database - into Python strings - (`issue 385 `__). - Initial work was done in `PR 549 - `__. -#) Enhanced dead connection detection. If an Oracle Database error indicates - that a connection is no longer usable, the error `DPI-1080: connection was - closed by ORA-%d` is now returned. The `%d` will be the Oracle error - causing the connection to be closed. Using the connection after this will - give `DPI-1010: not connected`. This behavior also applies for - :data:`Connection.call_timeout` errors that result in an unusable - connection. -#) Eliminated a memory leak when calling :meth:`SodaOperation.filter()` with a - dictionary. -#) The distributed transaction handle assosciated with the connection is now - cleared on commit or rollback (`issue 530 - `__). -#) Added a check to ensure that when setting variables or object attributes, - the type of the temporary LOB must match the expected type. -#) A small number of parameter, method, and attribute names were updated to - follow the PEP 8 style guide. This brings better consistency to the - cx_Oracle API. The old names are still usable but may be removed in a - future release of cx_Oracle. See :ref:`_deprecations_8_2` for details. -#) Improved the test suite. - - -Version 8.1 (December 2020) ---------------------------- - -#) Updated embedded ODPI-C to `version 4.1.0 - `__. -#) Added support for new JSON data type available in Oracle Client and - Database 21 and higher. -#) Dropped support for Python 3.5. Added support for Python 3.9. -#) Added internal methods for getting/setting OCI attributes that are - otherwise not supported by cx_Oracle. These methods should only be used as - directed by Oracle. -#) Minor code improvement supplied by Alex Henrie - (`PR 472 `__). -#) Builds are now done with setuptools and most metadata has moved from - `setup.py` to `setup.cfg` in order to take advantage of Python packaging - improvements. -#) The ability to pickle/unpickle Database and API types has been restored. -#) Tests can now be run with tox in order to automate testing of the different - environments that are supported. -#) The value of prefetchrows for REF CURSOR variables is now honored. -#) Improved documentation, samples and test suite. - - -Version 8.0.1 (August 2020) ---------------------------- - -#) Updated embedded ODPI-C to `version 4.0.2 - `__. This includes the fix for binding and - fetching numbers with 39 or 40 decimal digits - (`issue 459 `__). -#) Added build metadata specifying that Python 3.5 and higher is required in - order to avoid downloading and failing to install with Python 2. The - exception message when running ``setup.py`` directly was updated to inform - those using Python 2 to use version 7.3 instead. -#) Documentation improvements. - - -Version 8.0 (June 2020) ------------------------ - -#) Dropped support for Python 2. For those still requiring Python 2, see - :ref:`python2`. -#) Updated embedded ODPI-C to `version 4.0.1 - `__. -#) Reworked type management to clarify and simplify code - - - Added :ref:`constants ` for all database types. The database - types :data:`cx_Oracle.DB_TYPE_BINARY_FLOAT`, - :data:`cx_Oracle.DB_TYPE_INTERVAL_YM`, - :data:`cx_Oracle.DB_TYPE_TIMESTAMP_LTZ` and - :data:`cx_Oracle.DB_TYPE_TIMESTAMP_TZ` are completely new. The other - types were found in earlier releases under a different name. These types - will be found in :data:`Cursor.description` and passed as the defaultType - parameter to the :data:`Connection.outputtypehandler` and - :data:`Cursor.outputtypehandler` functions. - - Added :ref:`synonyms ` from the old type names to the new - type names for backwards compatibility. They are deprecated and will be - removed in a future version of cx_Oracle. - - The DB API :ref:`constants ` are now a specialized constant that - matches to the corresponding database types, as recommended by the DB - API. - - The variable attribute :data:`~Variable.type` now refers to one of the - new database type constants if the variable does not contain objects - (previously it was None in that case). - - The attribute :data:`~LOB.type` was added to LOB values. - - The attribute :data:`~ObjectAttribute.type` was added to attributes of - object types. - - The attribute :data:`~ObjectType.element_type` was added to object types. - - :ref:`Object types ` now compare equal if they were created - by the same connection or session pool and their schemas and names match. - - All variables are now instances of the same class (previously each type - was an instance of a separate variable type). The attribute - :data:`~Variable.type` can be examined to determine the database type it - is associated with. - - The string representation of variables has changed to include the type - in addition to the value. - -#) Added function :meth:`cx_Oracle.init_oracle_client()` in order to enable - programmatic control of the initialization of the Oracle Client library. -#) The default encoding for all character data is now UTF-8 and any character - set specified in the environment variable ``NLS_LANG`` is ignored. -#) Added functions :meth:`SodaCollection.save()`, - :meth:`SodaCollection.saveAndGet()` and :meth:`SodaCollection.truncate()` - available in Oracle Client 20 and higher. -#) Added function :meth:`SodaOperation.fetchArraySize()` available in Oracle - Client 19.5 and higher. -#) Added attribute :attr:`Cursor.prefetchrows` to control the number of rows - that the Oracle Client library fetches into internal buffers when a query - is executed. -#) Internally make use of new mode available in Oracle Client 20 and higher in - order to avoid a round-trip when accessing :attr:`Connection.version` for - the first time. -#) Added support for starting up a database using a parameter file (PFILE), - as requested - (`issue 295 `__). -#) Fixed overflow issue when calling :meth:`Cursor.getbatcherrors()` with - row offsets exceeding 65536. -#) Eliminated spurious error when accessing :attr:`Cursor.lastrowid` after - executing an INSERT ALL statement. -#) Miscellaneous improvements supplied by Alex Henrie (pull requests - `419 `__, - `420 `__, - `421 `__, - `422 `__, - `423 `__, - `437 `__ and - `438 `__). -#) Python objects bound to boolean variables are now converted to True or - False based on whether they would be considered True or False in a Python - if statement. Previously, only True was treated as True and all other - Python values (including 1, 1.0, and "foo") were treated as False - (pull request - `435 `__). -#) Documentation, samples and test suite improvements. - - -Version 7.3 (December 2019) ---------------------------- - -#) Added support for Python 3.8. -#) Updated embedded ODPI-C to `version 3.3 - `__. -#) Added support for CQN and other subscription client initiated connections - to the database (as opposed to the default server initiated connections) - created by calling :meth:`Connection.subscribe()`. -#) Added :attr:`support ` for returning the rowid of the - last row modified by an operation on a cursor (or None if no row was - modified). -#) Added support for setting the maxSessionsPerShard attribute when - :meth:`creating session pools `. -#) Added check to ensure sharding key is specified when a super sharding key - is specified. -#) Improved error message when the Oracle Client library is loaded - successfully but the attempt to detect the version of that library fails, - either due to the fact that the library is too old or the method could not - be called for some reason (`node-oracledb issue 1168 - `__). -#) Adjusted support for creating a connection using an existing OCI service - context handle. In order to avoid potential memory corruption and - unsupported behaviors, the connection will now use the same encoding as the - existing OCI service context handle when it was created. -#) Added ``ORA-3156: OCI call timed out`` to the list of error messages that - result in error DPI-1067. -#) Adjusted samples and the test suite so that they can be run against Oracle - Cloud databases. -#) Fixed bug when attempting to create a scrollable cursor on big endian - platforms like AIX on PPC. -#) Eliminated reference leak and ensure that memory is properly initialized in - case of error when using sharding keys. -#) Eliminated reference leak when splitting the password and DSN components - out of a full connect string. -#) Corrected processing of DATE sharding keys (sharding requires a slightly - different format to be passed to the server). -#) Eliminated reference leak when - :meth:`creating message property objects `. -#) Attempting to use proxy authentication with a homogeneous pool will now - raise a ``DatabaseError`` exception with the message - ``DPI-1012: proxy authentication is not possible with homogeneous pools`` - instead of a ``ProgrammingError`` exception with the message - ``pool is homogeneous. Proxy authentication is not possible.`` since this - check is done by ODPI-C. An empty string (or None) for the user name will - no longer generate an exception. -#) Exception ``InterfaceError: not connected`` is now always raised when an - operation is attempted with a closed connection. Previously, a number of - different exceptions were raised depending on the operation. -#) Added ``ORA-40479: internal JSON serializer error`` to the list of - exceptions that result in ``cx_Oracle.IntegrityError``. -#) Improved documentation. - - -Version 7.2.3 (October 2019) ----------------------------- - -#) Updated embedded ODPI-C to `version 3.2.2 - `__. -#) Restored support for setting numeric bind variables with boolean values. -#) Ensured that sharding keys are dedicated to the connection that is acquired - using them in order to avoid possible hangs, crashes or unusual errors. -#) Corrected support for PLS_INTEGER and BINARY_INTEGER types when used in - PL/SQL records - (`ODPI-C issue 112 `__). -#) Improved documentation. - - -Version 7.2.2 (August 2019) ---------------------------- - -#) Updated embedded ODPI-C to `version 3.2.1 - `__. -#) A more meaningful error is now returned when calling - :meth:`SodaCollection.insertMany()` with an empty list. -#) A more meaningful error is now returned when calling - :meth:`Subscription.registerquery()` with SQL that is not a SELECT - statement. -#) Eliminated segfault when a connection is closed after being created by a - call to :meth:`cx_Oracle.connect()` with the parameter ``cclass`` set to - a non-empty string. -#) Added user guide documentation. -#) Updated default connect strings to use 19c and XE 18c defaults. - - -Version 7.2.1 (July 2019) -------------------------- - -#) Resolved ``MemoryError`` exception on Windows when using an output type - handler - (`issue 330 `__). -#) Improved test suite and samples. -#) Improved documentation. - - -Version 7.2 (July 2019) ------------------------ - -#) Updated embedded ODPI-C to `version 3.2 - `__. -#) Improved AQ support - - - added support for enqueue and dequeue of RAW payloads - - added support for bulk enqueue and dequeue of messages - - added new method :meth:`Connection.queue()` which creates a new - :ref:`queue object ` in order to simplify AQ usage - - enhanced method :meth:`Connection.msgproperties()` to allow the writable - properties of the newly created object to be initialized. - - the original methods for enqueueing and dequeuing (Connection.deq(), - Connection.deqoptions(), Connection.enq() and Connection.enqoptions()) - are now deprecated and will be removed in a future version. - -#) Removed preview status from existing SODA functionality. See - `this tracking issue - `__ for known issues - with SODA. -#) Added support for a preview of SODA bulk insert, available in Oracle Client - 18.5 and higher. -#) Added support for setting LOB object attributes, as requested - (`issue 299 `__). -#) Added mode :data:`cx_Oracle.DEFAULT_AUTH` as requested - (`issue 293 `__). -#) Added support for using the LOB prefetch length indicator in order to - reduce the number of round trips when fetching LOBs and then subsequently - calling :meth:`LOB.size()`, :meth:`LOB.getchunksize()` or - :meth:`LOB.read()`. This is always enabled. -#) Added support for types BINARY_INTEGER, PLS_INTEGER, ROWID, LONG and LONG - RAW when used in PL/SQL. -#) Eliminated deprecation of attribute :attr:`Subscription.id`. It is now - populated with 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. -#) Enabled PY_SSIZE_T_CLEAN, as required by Python 3.8 - (`issue 317 `__). -#) Eliminated memory leak when fetching objects that are atomically null - (`issue 298 `__). -#) Eliminated bug when processing the string representation of numbers like - 1e-08 and 1e-09 (`issue 300 - `__). -#) Improved error message when the parent cursor is closed before a fetch is - attempted from an implicit result cursor. -#) Improved test suite and samples. -#) Improved documentation. - - -Version 7.1.3 (April 2019) --------------------------- - -#) Updated to `ODPI-C 3.1.4 - `__. -#) Added support for getting the row count for PL/SQL statements - (`issue 285 `__). -#) Corrected parsing of connect string so that the last @ symbol is searched - for instead of the first @ symbol; otherwise, passwords containing an @ - symbol will result in the incorrect DSN being extracted - (`issue 290 `__). -#) Adjusted return value of cursor.callproc() to follow documentation (only - positional arguments are returned since the order of keyword parameters - cannot be guaranteed in any case) - (`PR 287 `__). -#) Corrected code getting sample and test parameters by user input when using - Python 2.7. - - -Version 7.1.2 (March 2019) --------------------------- - -#) Updated to `ODPI-C 3.1.3 - `__. -#) Ensured that the strings "-0" and "-0.0" are correctly handled as zero - values - (`issue 274 `__). -#) Eliminated error when startup and shutdown events are generated - (`ODPI-C issue 102 `__). -#) Enabled the types specified in :meth:`Cursor.setinputsizes()` and - :meth:`Cursor.callfunc()` to be an object type in addition to a Python - type, just like in :meth:`Cursor.var()`. -#) Reverted changes to return decimal numbers when the numeric precision was - too great to be returned accurately as a floating point number. This change - had too great an impact on existing functionality and an output type - handler can be used to return decimal numbers where that is desirable - (`issue 279 `__). -#) Eliminated discrepancies in character sets between an external connection - handle and the newly created connection handle that references the external - connection handle - (`issue 273 `__). -#) Eliminated memory leak when receiving messages received from subscriptions. -#) Improved test suite and documentation. - - -Version 7.1.1 (February 2019) ------------------------------ - -#) Updated to `ODPI-C 3.1.2 - `__. -#) Corrected code for freeing CQN message objects when multiple queries are - registered - (`ODPI-C issue 96 `__). -#) Improved error messages and installation documentation. - - -Version 7.1 (February 2019) ---------------------------- - -#) Updated to `ODPI-C 3.1 - `__. -#) Improved support for session tagging in session pools by allowing a - session callback to be specified when creating a pool via - :meth:`cx_Oracle.SessionPool()`. Callbacks can be written in Python or in - PL/SQL and can be used to improve performance by decreasing round trips to - the database needed to set session state. Callbacks written in Python will - be invoked for brand new connections (that have never been acquired from - the pool before) or when the tag assigned to the connection doesn't match - the one that was requested. Callbacks written in PL/SQL will only be - invoked when the tag assigned to the connection doesn't match the one that - was requested. -#) Added attribute :attr:`Connection.tag` to provide access to the actual tag - assigned to the connection. Setting this attribute will cause the - connection to be retagged when it is released back to the pool. -#) Added support for fetching SYS.XMLTYPE values as strings, as requested - (`issue 14 `__). - Note that this support is limited to the size of VARCHAR2 columns in the - database (either 4000 or 32767 bytes). -#) Added support for allowing the typename parameter in method - :meth:`Cursor.var()` to be None or a valid object type created by the - method :meth:`Connection.gettype()`, as requested - (`issue 231 `__). -#) Added support for getting and setting attributes of type RAW on Oracle - objects, as requested - (`ODPI-C issue 72 `__). -#) Added support for performing external authentication with proxy for - standalone connections. -#) Added support for mixing integers, floating point and decimal values in - data passed to :meth:`Cursor.executemany()` - (`issue 241 `__). - The error message raised when a value cannot be converted to an Oracle - number was also improved. -#) Adjusted fetching of numeric values so that no precision is lost. If an - Oracle number cannot be represented by a Python floating point number a - decimal value is automatically returned instead. -#) Corrected handling of multiple calls to method - :meth:`Cursor.executemany()` where all of the values in one of the columns - passed to the first call are all None and a subsequent call has a value - other than None in the same column - (`issue 236 `__). -#) Added additional check for calling :meth:`Cursor.setinputsizes()` with an - empty dictionary in order to avoid the error "cx_Oracle.ProgrammingError: - positional and named binds cannot be intermixed" - (`issue 199 `__). -#) Corrected handling of values that exceed the maximum value of a plain - integer object on Python 2 on Windows - (`issue 257 `__). -#) Added error message when attempting external authentication with proxy - without placing the user name in [] (proxy authentication was previously - silently ignored). -#) Exempted additional error messages from forcing a statement to be dropped - from the cache - (`ODPI-C issue 76 `__). -#) Improved dead session detection when using session pools for Oracle Client - 12.2 and higher. -#) Ensured that the connection returned from a pool after a failed ping (such - as due to a killed session) is not itself marked as needing to be dropped - from the pool. -#) Eliminated memory leak under certain circumstances when pooled connections - are released back to the pool. -#) Eliminated memory leak when connections are dropped from the pool. -#) Eliminated memory leak when calling :meth:`Connection.close()` after - fetching collections from the database. -#) Adjusted order in which memory is freed when the last references to SODA - collections, documents, document cursors and collection cursors are - released, in order to prevent a segfault under certain circumstances. -#) Improved code preventing a statement from binding itself, in order to avoid - a potential segfault under certain circumstances. -#) Worked around OCI bug when attempting to free objects that are PL/SQL - records, in order to avoid a potential segfault. -#) Improved test suite and samples. Note that default passwords are no longer - supplied. New environment variables can be set to specify passwords if - desired, or the tests and samples will prompt for the passwords when - needed. In addition, a Python script is now available to create and drop - the schemas used for the tests and samples. -#) Improved documentation. - - -Version 7.0 (September 2018) ----------------------------- - -#) Update to `ODPI-C 3.0 - `__. -#) Added support for Oracle Client 18 libraries. -#) Added support for SODA (as preview). See :ref:`SODA Database `, - :ref:`SODA Collection ` and :ref:`SODA Document ` for - more information. -#) Added support for call timeouts available in Oracle Client 18.1 and - higher. See :attr:`Connection.call_timeout`. -#) Added support for getting the contents of a SQL collection object as a - dictionary, where the keys are the indices of the collection and the values - are the elements of the collection. See function :meth:`Object.asdict()`. -#) Added support for closing a session pool via the function - :meth:`SessionPool.close()`. Once closed, further attempts to use any - connection that was acquired from the pool will result in the error - "DPI-1010: not connected". -#) Added support for setting a LOB attribute of an object with a string or - bytes (instead of requiring a temporary LOB to be created). -#) Added support for the packed decimal type used by object attributes with - historical types DECIMAL and NUMERIC - (`issue 212 `__). -#) On Windows, first attempt to load oci.dll from the same directory as - the cx_Oracle module. -#) SQL objects that are created or fetched from the database are now tracked - and marked unusable when a connection is closed. This was done in order - to avoid a segfault under certain circumstances. -#) Re-enabled dead session detection functionality when using pools for Oracle - Client 12.2 and higher in order to handle classes of connection errors such - as resource profile limits. -#) Improved error messages when the Oracle Client or Oracle Database need to - be at a minimum version in order to support a particular feature. -#) When a connection is used as a context manager, the connection is now - closed when the block ends. Attempts to set - ``cx_Oracle.__future__.ctx_mgr_close`` are now ignored. -#) When a DML returning statement is executed, variables bound to it will - return an array when calling :meth:`Variable.getvalue()`. Attempts to set - ``cx_Oracle.__future__.dml_ret_array_val`` are now ignored. -#) Support for Python 3.4 has been dropped. -#) Added additional test cases. -#) Improved documentation. - - -Version 6.4.1 (July 2018) -------------------------- - -#) Update to `ODPI-C 2.4.2 - `__. - - - Avoid buffer overrun due to improper calculation of length byte when - converting some negative 39 digit numbers from string to the internal - Oracle number format - (`ODPI-C issue 67 `__). - -#) Prevent error "cx_Oracle.ProgrammingError: positional and named binds - cannot be intermixed" when calling cursor.setinputsizes() without any - parameters and then calling cursor.execute() with named bind parameters - (`issue 199 `__). - - -Version 6.4 (July 2018) ------------------------ - -#) Update to `ODPI-C 2.4.1 - `__. - - - Added support for grouping subscriptions. See parameters groupingClass, - groupingValue and groupingType to function - :meth:`Connection.subscribe()`. - - Added support for specifying the IP address a subscription should use - instead of having the Oracle Client library determine the IP address on - its own. See parameter ipAddress to function - :meth:`Connection.subscribe()`. - - Added support for subscribing to notifications when messages are - available to dequeue in an AQ queue. The new constant - :data:`cx_Oracle.SUBSCR_NAMESPACE_AQ` should be passed to the namespace - parameter of function :meth:`Connection.subscribe()` in order to get this - functionality. Attributes :attr:`Message.queueName` and - :attr:`Message.consumerName` will be populated in notification messages - that are received when this namespace is used. - - Added attribute :attr:`Message.registered` to let the notification - callback know when the subscription that generated the notification is no - longer registered with the database. - - Added support for timed waits when acquiring a session from a session - pool. Use the new constant :data:`cx_Oracle.SPOOL_ATTRVAL_TIMEDWAIT` in - the parameter getmode to function :meth:`cx_Oracle.SessionPool` along - with the new parameter waitTimeout. - - Added support for specifying the timeout and maximum lifetime session for - session pools when they are created using function - :meth:`cx_Oracle.SessionPool`. Previously the pool had to be created - before these values could be changed. - - Avoid memory leak when dequeuing from an empty queue. - - Ensure that the row count for queries is reset to zero when the statement - is executed - (`issue 193 `__). - - If the statement should be deleted from the statement cache, first check - to see that there is a statement cache currently being used; otherwise, - the error "ORA-24300: bad value for mode" will be raised under certain - conditions. - -#) Added support for using the cursor as a context manager - (`issue 190 `__). -#) Added parameter "encodingErrors" to function :meth:`Cursor.var()` in order - to add support for specifying the "errors" parameter to the decode() that - takes place internally when fetching strings from the database - (`issue 162 `__). -#) Added support for specifying an integer for the parameters argument to - :meth:`Cursor.executemany()`. This allows for batch execution when no - parameters are required or when parameters have previously been bound. This - replaces Cursor.executemanyprepared() (which is now deprecated and will be - removed in cx_Oracle 7). -#) Adjusted the binding of booleans so that outside of PL/SQL they are bound - as integers - (`issue 181 `__). -#) Added support for binding decimal.Decimal values to cx_Oracle.NATIVE_FLOAT - as requested - (`issue 184 `__). -#) Added checks on passing invalid type parameters to methods - :meth:`Cursor.arrayvar()`, :meth:`Cursor.callfunc()` and - :meth:`Cursor.setinputsizes()`. -#) Corrected handling of cursors and rowids in DML Returning statements. -#) Added sample from David Lapp demonstrating the use of GeoPandas with - SDO_GEOMETRY and a sample for demonstrating the use of REF cursors. -#) Adjusted samples and documentation for clarity. -#) Added additional test cases. - - -Version 6.3.1 (May 2018) ------------------------- - -#) Update to `ODPI-C 2.3.2 - `__. - - - Ensure that a call to unregister a subscription only occurs if the - subscription is still registered. - - Ensure that before a statement is executed any buffers used for DML - returning statements are reset. - -#) Ensure that behavior with cx_Oracle.__future__.dml_ret_array_val not - set or False is the same as the behavior in cx_Oracle 6.2 - (`issue 176 `__). - - -Version 6.3 (April 2018) ------------------------- - -#) Update to `ODPI-C 2.3.1 - `__. - - - Fixed binding of LONG data (values exceeding 32KB) when using the - function :meth:`Cursor.executemany()`. - - Added code to verify that a CQN subscription is open before permitting it - to be used. Error "DPI-1060: subscription was already closed" will now be - raised if an attempt is made to use a subscription that was closed - earlier. - - Stopped attempting to unregister a CQN subscription before it was - completely registered. This prevents errors encountered during - registration from being masked by an error stating that the subscription - has not been registered! - - Added error "DPI-1061: edition is not supported when a new password is - specified" to clarify the fact that specifying an edition and a new - password at the same time is not supported when creating a connection. - Previously the edition value was simply ignored. - - Improved error message when older OCI client libraries are being used - that don't have the method OCIClientVersion(). - - Fixed the handling of ANSI types REAL and DOUBLE PRECISION as - implemented by Oracle. These types are just subtypes of NUMBER and are - different from BINARY_FLOAT and BINARY_DOUBLE - (`issue 163 `__). - - Fixed support for true heterogeneous session pools that use different - user/password combinations for each session acquired from the pool. - - Added error message indicating that setting either of the parameters - arraydmlrowcounts and batcherrors to True in :meth:`Cursor.executemany()` - is only supported with insert, update, delete and merge statements. - -#) Fixed support for getting the OUT values of bind variables bound to a DML - Returning statement when calling the function :meth:`Cursor.executemany()`. - Note that the attribute dml_ret_array_val in :attr:`cx_Oracle.__future__` - must be set to True first. -#) Added support for binding integers and floats as cx_Oracle.NATIVE_FLOAT. -#) A :attr:`cx_Oracle._Error` object is now the value of all cx_Oracle - exceptions raised by cx_Oracle. - (`issue 51 `__). -#) Added support for building cx_Oracle with a pre-compiled version of ODPI-C, - as requested - (`issue 103 `__). -#) Default values are now provided for all parameters to - :meth:`cx_Oracle.SessionPool`. -#) Improved error message when an unsupported Oracle type is encountered. -#) The Python GIL is now prevented from being held while performing a round - trip for the call to get the attribute :attr:`Connection.version` - (`issue 158 `__). -#) Added check for the validity of the year for Python 2.x since it doesn't do - that itself like Python 3.x does - (`issue 166 `__). -#) Adjusted documentation to provide additional information on the use of - :meth:`Cursor.executemany()` as requested - (`issue 153 `__). -#) Adjusted documentation to state that batch errors and array DML row counts - can only be used with insert, update, delete and merge statements - (`issue 31 `__). -#) Updated tutorial to import common connection information from files in - order to make setup a bit more generic. - - -Version 6.2.1 (March 2018) --------------------------- - -#) Make sure cxoModule.h is included in the source archive - (`issue 155 `__). - - -Version 6.2 (March 2018) ------------------------- - -#) Update to `ODPI-C 2.2.1 - `__. - - - eliminate error "DPI-1054: connection cannot be closed when open - statements or LOBs exist" (`issue 138 - `__). - - avoid a round trip to the database when a connection is released back to - the pool by preventing a rollback from being called when no transaction - is in progress. - - improve error message when the use of bind variables is attempted with - DLL statements, which is not supported by Oracle. - - if an Oracle object is retrieved from an attribute of another Oracle - object or a collection, prevent the "owner" from being destroyed until - the object that was retrieved has itself been destroyed. - - correct handling of boundary numbers 1e126 and -1e126 - - eliminate memory leak when calling :meth:`Connection.enq()` and - :meth:`Connection.deq()` - - eliminate memory leak when setting NCHAR and NVARCHAR attributes of - objects. - - eliminate memory leak when fetching collection objects from the database. - -#) Added support for creating a temporary CLOB, BLOB or NCLOB via the method - :meth:`Connection.createlob()`. -#) Added support for binding a LOB value directly to a cursor. -#) Added support for closing the connection when reaching the end of a - ``with`` code block controlled by the connection as a context manager, but - in a backwards compatible way - (`issue 113 `__). - See :data:`cx_Oracle.__future__` for more information. -#) Reorganized code to simplify continued maintenance and consolidate - transformations to/from Python objects. -#) Ensure that the number of elements in the array is not lost when the - buffer size is increased to accommodate larger strings. -#) Corrected support in Python 3.x for cursor.parse() by permitting a string - to be passed, instead of incorrectly requiring a bytes object. -#) Eliminate reference leak with LOBs acquired from attributes of objects or - elements of collections. -#) Eliminate reference leak when extending an Oracle collection. -#) Documentation improvements. -#) Added test cases to the test suite. - - -Version 6.1 (December 2017) ---------------------------- - -#) Update to `ODPI-C 2.1 - `__. - - - Support was added for accessing sharded databases via sharding keys (new - in Oracle 12.2). NOTE: the underlying OCI library has a bug when using - standalone connections. There is a small memory leak proportional to the - number of connections created/dropped. There is no memory leak when using - session pools, which is recommended. - - Added options for authentication with SYSBACKUP, SYSDG, SYSKM and SYSRAC, - as requested (`issue 101 - `__). - - Attempts to release statements or free LOBs after the connection has been - closed (by, for example, killing the session) are now prevented. - - An error message was added when specifying an edition and a connection - class since this combination is not supported. - - Attempts to close the session for connections created with an external - handle are now prevented. - - Attempting to ping a database earlier than 10g results in ORA-1010: - invalid OCI operation, but that implies a response from the database and - therefore a successful ping, so treat it that way! - (see ``__ for more information). - - Support was added for converting numeric values in an object type - attribute to integer and text, as requested (`ODPI-C issue 35 - `__). - - Setting attributes :attr:`DeqOptions.msgId` and - :attr:`MessageProperties.msgId` now works as expected. - - The overflow check when using double values (Python floats) as input - to float attributes of objects or elements of collections was removed as - it didn't work anyway and is a well-known issue that cannot be prevented - without removing desired functionality. The developer should ensure that - the source value falls within the limits of floats, understand the - consequent precision loss or use a different data type. - - Variables of string/raw types are restricted to 2 bytes less than 1 GB - (1,073,741,822 bytes), since OCI cannot handle more than that currently. - - Support was added for identifying the id of the transaction which spawned - a CQN subscription message, as requested - (`ODPI-C issue 32 `__). - - Corrected use of subscription port number (`issue 115 - `__). - - Problems reported with the usage of FormatMessage() on Windows were - addressed (`ODPI-C issue 47 - `__). - - On Windows, if oci.dll cannot be loaded because it is the wrong - architecture (32-bit vs 64-bit), attempt to find the offending DLL and - include the full path of the DLL in the message, as suggested. - (`ODPI-C issue 49 `__). - - Force OCI prefetch to always use the value 2; the OCI default is 1 but - setting the ODPI-C default to 2 ensures that single row fetches don't - require an extra round trip to determine if there are more rows to fetch; - this change also reduces the potential memory consumption when - fetchArraySize was set to a large value and also avoids performance - issues discovered with larger values of prefetch. - -#) Fix build with PyPy 5.9.0-alpha0 in libpython mode - (`PR 54 `__). -#) Ensure that the edition is passed through to the database when a session - pool is created. -#) Corrected handling of Python object references when an invalid keyword - parameter is passed to :meth:`cx_Oracle.SessionPool`. -#) Corrected handling of :attr:`Connection.handle` and the handle parameter - to :meth:`cx_Oracle.connect` on Windows. -#) Documentation improvements. -#) Added test cases to the test suite. - - -Version 6.0.3 (November 2017) ------------------------------ - -#) Update to `ODPI-C 2.0.3 - `__. - - - Prevent use of uninitialized data in certain cases (`issue 77 - `__). - - Attempting to ping a database earlier than 10g results in error - "ORA-1010: invalid OCI operation", but that implies a response from the - database and therefore a successful ping, so treat it that way! - - Correct handling of conversion of some numbers to NATIVE_FLOAT. - - Prevent use of NaN with Oracle numbers since it produces corrupt data - (`issue 91 `__). - - Verify that Oracle objects bound to cursors, fetched from cursors, set in - object attributes or appended to collection objects are of the correct - type. - - Correct handling of NVARCHAR2 when used as attributes of Oracle objects - or as elements of collections. - -#) Ensure that a call to setinputsizes() with an invalid type prior to a call - to executemany() does not result in a type error, but instead gracefully - ignores the call to setinputsizes() as required by the DB API - (`issue 75 `__). -#) Check variable array size when setting variable values and raise - IndexError, as is already done for getting variable values. - - -Version 6.0.2 (August 2017) ---------------------------- - -#) Update to `ODPI-C 2.0.2 - `__. - - - Don't prevent connection from being explicitly closed when a fatal error - has taken place (`issue 67 - `__). - - Correct handling of objects when dynamic binding is performed. - - Process deregistration events without an error. - - Eliminate memory leak when creating objects. - -#) Added missing type check to prevent coercion of decimal to float - (`issue 68 `__). -#) On Windows, sizeof(long) = 4, not 8, which meant that integers between 10 - and 18 digits were not converted to Python correctly - (`issue 70 `__). -#) Eliminate memory leak when repeatedly executing the same query. -#) Eliminate segfault when attempting to reuse a REF cursor that has been - closed. -#) Updated documentation. - - -Version 6.0.1 (August 2017) ---------------------------- - -#) Update to `ODPI-C 2.0.1 - `__. - - - Ensure that queries registered via :meth:`Subscription.registerquery()` - do not prevent the associated connection from being closed - (`ODPI-C issue 27 `__). - - Deprecated attribute :attr:`Subscription.id` as it was never intended to - be exposed (`ODPI-C issue 28 - `__). It will be dropped in - version 6.1. - - Add support for DML Returning statements that require dynamically - allocated variable data (such as CLOBs being returned as strings). - -#) Correct packaging of Python 2.7 UCS4 wheels on Linux - (`issue 64 `__). -#) Updated documentation. - - -Version 6.0 (August 2017) -------------------------- - -#) Update to `ODPI-C 2.0 `__. - - - Prevent closing the connection when there are any open statements or - LOBs and add new error "DPI-1054: connection cannot be closed when open - statements or LOBs exist" when this situation is detected; this is - needed to prevent crashes under certain conditions when statements or - LOBs are being acted upon while at the same time (in another thread) a - connection is being closed; it also prevents leaks of statements and - LOBs when a connection is returned to a session pool. - - On platforms other than Windows, if the regular method for loading the - Oracle Client libraries fails, try using $ORACLE_HOME/lib/libclntsh.so - (`ODPI-C issue 20 `__). - - Use the environment variable DPI_DEBUG_LEVEL at runtime, not compile - time. - - Added support for DPI_DEBUG_LEVEL_ERRORS (reports errors and has the - value 8) and DPI_DEBUG_LEVEL_SQL (reports prepared SQL statement text - and has the value 16) in order to further improve the ability to debug - issues. - - Correct processing of :meth:`Cursor.scroll()` in some circumstances. - -#) Delay initialization of the ODPI-C library until the first standalone - connection or session pool is created so that manipulation of the - environment variable NLS_LANG can be performed after the module has been - imported; this also has the added benefit of reducing the number of errors - that can take place when the module is imported. -#) Prevent binding of null values from generating the exception "ORA-24816: - Expanded non LONG bind data supplied after actual LONG or LOB column" in - certain circumstances - (`issue 50 `__). -#) Added information on how to run the test suite - (`issue 33 `__). -#) Documentation improvements. - - -Version 6.0 rc 2 (July 2017) ----------------------------- - -#) Update to `ODPI-C rc 2 `__. - - - Provide improved error message when OCI environment cannot be created, - such as when the oraaccess.xml file cannot be processed properly. - - On Windows, convert system message to Unicode first, then to UTF-8; - otherwise, the error message returned could be in a mix of encodings - (`issue 40 `__). - - Corrected support for binding decimal values in object attribute values - and collection element values. - - Corrected support for binding PL/SQL boolean values to PL/SQL - procedures with Oracle client 11.2. - -#) Define exception classes on the connection object in addition to at module - scope in order to simplify error handling in multi-connection environments, - as specified in the Python DB API. -#) Ensure the correct encoding is used for setting variable values. -#) Corrected handling of CLOB/NCLOB when using different encodings. -#) Corrected handling of TIMESTAMP WITH TIME ZONE attributes on objects. -#) Ensure that the array position passed to var.getvalue() does not exceed the - number of elements allocated in the array. -#) Reworked test suite and samples so that they are independent of each other - and so that the SQL scripts used to create/drop schemas are easily adjusted - to use different schema names, if desired. -#) Updated DB API test suite stub to support Python 3. -#) Added additional test cases and samples. -#) Documentation improvements. - - -Version 6.0 rc 1 (June 2017) ----------------------------- - -#) Update to `ODPI-C rc 1 `__. -#) The method :meth:`Cursor.setoutputsize` no longer needs to do anything, - since ODPI-C automatically manages buffer sizes of LONG and LONG RAW - columns. -#) Handle case when both precision and scale are zero, as occurs when - retrieving numeric expressions (`issue 34 - `__). -#) OCI requires that both encoding and nencoding have values or that both - encoding and encoding do not have values. These parameters are used in - functions :meth:`cx_Oracle.connect` and :meth:`cx_Oracle.SessionPool`. The - missing value is set to its default value if one of the values is set and - the other is not (`issue 36 - `__). -#) Permit use of both string and unicode for Python 2.7 for creating session - pools and for changing passwords (`issue 23 - `__). -#) Corrected handling of BFILE LOBs. -#) Add script for dropping test schemas. -#) Documentation improvements. - - -Version 6.0 beta 2 (May 2017) ------------------------------ - -#) Added support for getting/setting attributes of objects or element values - in collections that contain LOBs, BINARY_FLOAT values, BINARY_DOUBLE values - and NCHAR and NVARCHAR2 values. The error message for any types that are - not supported has been improved as well. -#) Enable temporary LOB caching in order to avoid disk I/O as - `suggested `__. -#) Added support for setting the debug level in ODPI-C, if desirable, by - setting environment variable DPI_DEBUG_LEVEL prior to building cx_Oracle. -#) Correct processing of strings in :meth:`Cursor.executemany` when a - larger string is found after a shorter string in the list of data bound to - the statement. -#) Correct handling of long Python integers that cannot fit inside a 64-bit C - integer (`issue 18 - `__). -#) Correct creation of pool using external authentication. -#) Handle edge case when an odd number of zeroes trail the decimal point in a - value that is effectively zero (`issue 22 - `__). -#) Prevent segfault under load when the attempt to create an error fails. -#) Eliminate resource leak when a standalone connection or pool is freed. -#) Correct `typo `__. -#) Correct handling of REF cursors when the array size is manipulated. -#) Prevent attempts from binding the cursor being executed to itself. -#) Correct reference count handling of parameters when creating a cursor. -#) Correct determination of the names of the bind variables in prepared SQL - statements (which behaves a little differently from PL/SQL statements). - - -Version 6.0 beta 1 (April 2017) -------------------------------- - -#) Simplify building cx_Oracle considerably by use of - `ODPI-C `__. This means that cx_Oracle can - now be built without Oracle Client header files or libraries and that at - runtime cx_Oracle can adapt to Oracle Client 11.2, 12.1 or 12.2 libraries - without needing to be rebuilt. This also means that wheels can now be - produced and installed via pip. -#) Added attribute :attr:`SessionPool.stmtcachesize` to support getting and - setting the default statement cache size for connections in the pool. -#) Added attribute :attr:`Connection.dbop` to support setting the database - operation that is to be monitored. -#) Added attribute :attr:`Connection.handle` to facilitate testing the - creation of a connection using a OCI service context handle. -#) Added parameters tag and matchanytag to the :meth:`cx_Oracle.connect` - and :meth:`SessionPool.acquire` methods and added parameters tag and retag - to the :meth:`SessionPool.release` method in order to support session - tagging. -#) Added parameter edition to the :meth:`cx_Oracle.SessionPool` method. -#) Added support for - `universal rowids `__. -#) Added support for `DML Returning of multiple rows - `__. -#) Added attributes :attr:`Variable.actualElements` and - :attr:`Variable.values` to variables. -#) Added parameters region, sharding_key and super_sharding_key to the - :meth:`cx_Oracle.makedsn()` method to support connecting to a sharded - database (new in Oracle Database 12.2). -#) Added support for smallint and float data types in Oracle objects, as - `requested `__. -#) An exception is no longer raised when a collection is empty for methods - :meth:`Object.first()` and :meth:`Object.last()`. Instead, the value None - is returned to be consistent with the methods :meth:`Object.next()` and - :meth:`Object.prev()`. -#) If the environment variables NLS_LANG and NLS_NCHAR are being used, they - must be set before the module is imported. Using the encoding and nencoding - parameters to the :meth:`cx_Oracle.connect` and - :meth:`cx_Oracle.SessionPool` methods is a simpler alternative to setting - these environment variables. -#) Removed restriction on fetching LOBs across round trips to the database - (eliminates error "LOB variable no longer valid after subsequent fetch"). -#) Removed requirement for specifying a maximum size when fetching LONG or - LONG raw columns. This also allows CLOB, NCLOB, BLOB and BFILE columns to - be fetched as strings or bytes without needing to specify a maximum size. -#) Dropped deprecated parameter twophase from the :meth:`cx_Oracle.connect` - method. Applications should set the :attr:`Connection.internal_name` and - :attr:`Connection.external_name` attributes instead to a value appropriate - to the application. -#) Dropped deprecated parameters action, module and clientinfo from the - :meth:`cx_Oracle.connect` method. The appcontext parameter should be used - instead as shown in this `sample `__. -#) Dropped deprecated attribute numbersAsString from - :ref:`cursor objects `. Use an output type handler instead as - shown in this `sample `__. -#) Dropped deprecated attributes cqqos and rowids from - :ref:`subscription objects `. Use the qos attribute instead as - shown in this `sample `__. -#) Dropped deprecated parameters cqqos and rowids from the - :meth:`Connection.subscribe()` method. Use the qos parameter instead as - shown in this `sample `__. - - -Version 5.3 (March 2017) ------------------------- - -#) Added support for Python 3.6. -#) Dropped support for Python versions earlier than 2.6. -#) Dropped support for Oracle clients earlier than 11.2. -#) Added support for - :meth:`fetching implicit results` - (available in Oracle 12.1) -#) Added support for :attr:`Transaction Guard ` (available - in Oracle 12.1). -#) Added support for setting the - :attr:`maximum lifetime ` of pool - connections (available in Oracle 12.1). -#) Added support for large row counts (larger than 2 ** 32, available in - Oracle 12.1) -#) Added support for :meth:`advanced queuing `. -#) Added support for :meth:`scrollable cursors `. -#) Added support for :attr:`edition based redefinition `. -#) Added support for :meth:`creating `, modifying and - binding user defined types and collections. -#) Added support for creating, modifying and binding PL/SQL records and - collections (available in Oracle 12.1). -#) Added support for binding :data:`native integers `. -#) Enabled statement caching. -#) Removed deprecated variable attributes maxlength and allocelems. -#) Corrected support for setting the encoding and nencoding parameters when - :meth:`creating a connection ` and added support for - setting these when creating a session pool. These can now be used instead - of setting the environment variables NLS_LANG and NLS_NCHAR. -#) Use None instead of 0 for items in the :attr:`Cursor.description` attribute - that do not have any validity. -#) Changed driver name to match informal driver name standard used by Oracle - for other drivers. -#) Add check for maximum of 10,000 parameters when calling a stored procedure - or function in order to prevent a possible improper memory access from - taking place. -#) Removed -mno-cygwin compile flag since it is no longer used in newer - versions of the gcc compiler for Cygwin. -#) Simplified test suite by combining Python 2 and 3 scripts into one script - and separated out 12.1 features into a single script. -#) Updated samples to use code that works on both Python 2 and 3 -#) Added support for pickling/unpickling error objects - (`Issue #23 `__) -#) Dropped support for callbacks on OCI functions. -#) Removed deprecated types UNICODE, FIXED_UNICODE and LONG_UNICODE (use - NCHAR, FIXED_NCHAR and LONG_NCHAR instead). -#) Increased default array size to 100 (from 50) to match other drivers. -#) Added support for setting the :attr:`~Connection.internal_name` and - :attr:`~Connection.external_name` on the connection directly. The use of - the twophase parameter is now deprecated. Applications should set the - internal_name and external_name attributes directly to a value appropriate - to the application. -#) Added support for using application context when - :meth:`creating a connection `. This should be used - in preference to the module, action and clientinfo parameters which are now - deprecated. -#) Reworked database change notification and continuous query notification to - more closely align with the PL/SQL implementation and prepare for sending - notifications for AQ messages. The following changes were made: - - - added constant :data:`~cx_Oracle.SUBSCR_QOS_BEST_EFFORT` to replace - deprecated constant SUBSCR_CQ_QOS_BEST_EFFORT - - added constant :data:`~cx_Oracle.SUBSCR_QOS_QUERY` to replace - deprecated constant SUBSCR_CQ_QOS_QUERY - - added constant :data:`~cx_Oracle.SUBSCR_QOS_DEREG_NFY` to replace - deprecated constant SUBSCR_QOS_PURGE_ON_NTFN - - added constant :data:`~cx_Oracle.SUBSCR_QOS_ROWIDS` to replace parameter - rowids for method :meth:`Connection.subscribe()` - - deprecated parameter cqqos for method :meth:`Connection.subscribe()`. The - qos parameter should be used instead. - - dropped constants SUBSCR_CQ_QOS_CLQRYCACHE, SUBSCR_QOS_HAREG, - SUBSCR_QOS_MULTICBK, SUBSCR_QOS_PAYLOAD, SUBSCR_QOS_REPLICATE, and - SUBSCR_QOS_SECURE since they were never actually used -#) Deprecated use of the numbersAsStrings attribute on cursors. An output type - handler should be used instead. - - -Version 5.2.1 (January 2016) ----------------------------- - -#) Added support for Python 3.5. -#) Removed password attribute from connection and session pool objects in - order to promote best security practices (if stored in RAM in cleartext it - can be read in process dumps, for example). For those who would like to - retain this feature, a subclass of Connection could be used to store the - password. -#) Added optional parameter externalauth to SessionPool() which enables wallet - based or other external authentication mechanisms to be used. -#) Use the national character set encoding when required (when char set form - is SQLCS_NCHAR); otherwise, the wrong encoding would be used if the - environment variable NLS_NCHAR is set. -#) Added support for binding boolean values to PL/SQL blocks and stored - procedures (available in Oracle 12.1). - - -Version 5.2 (June 2015) ------------------------ - -#) Added support for strings up to 32k characters (new in Oracle 12c). -#) Added support for getting array DML row counts (new in Oracle 12c). -#) Added support for fetching batch errors. -#) Added support for LOB values larger than 4 GB. -#) Added support for connections as SYSASM. -#) Added support for building without any configuration changes to the machine - when using instant client RPMs on Linux. -#) Added types NCHAR, FIXED_NCHAR and LONG_NCHAR to replace the types UNICODE, - FIXED_UNICODE and LONG_UNICODE (which are now deprecated). These types are - available in Python 3 as well so they can be used to specify the use of - NCHAR type fields when binding or using setinputsizes(). -#) Fixed binding of booleans in Python 3.x. -#) Test suite now sets NLS_LANG if not already set. -#) Enhanced documentation for connection.action attribute and added note - on cursor.parse() method to make clear that DDL statements are executed - when parsed. -#) Removed remaining remnants of support Oracle 9i. -#) Added __version__ attribute to conform with PEP 396. -#) Ensure that sessions are released to the pool when calling - connection.close() - (`Issue #2 `__) -#) Fixed handling of datetime intervals - (`Issue #7 `__) - - -Version 5.1.3 (May 2014) ------------------------- - -#) Added support for Oracle 12c. -#) Added support for Python 3.4. -#) Added support for query result set change notification. Thanks to Glen - Walker for the patch. -#) Ensure that in Python 3.x that NCHAR and NVARCHAR2 and NCLOB columns are - retrieved properly without conversion issues. Thanks to Joakim Andersson - for pointing out the issue and the possible solution. -#) Fix bug when an exception is caught and then another exception is raised - while handling that exception in Python 3.x. Thanks to Boris Dzuba for - pointing out the issue and providing a test case. -#) Enhance performance returning integers between 10 and 18 digits on 64-bit - platforms that support it. Thanks for Shai Berger for the initial patch. -#) Fixed two memory leaks. -#) Fix to stop current_schema from throwing a MemoryError on 64-bit platforms - on occasion. Thanks to Andrew Horton for the fix. -#) Class name of cursors changed to real name cx_Oracle.Cursor. - - -Version 5.1.2 (July 2012) -------------------------- - -#) Added support for LONG_UNICODE which is a type used to handle long unicode - strings. These are not explicitly supported in Oracle but can be used to - bind to NCLOB, for example, without getting the error "unimplemented or - unreasonable conversion requested". -#) Set the row number in a cursor when executing PL/SQL blocks as requested - by Robert Ritchie. -#) Added support for setting the module, action and client_info attributes - during connection so that logon triggers will see the supplied values, as - requested by Rodney Barnett. - - -Version 5.1.1 (October 2011) ----------------------------- - -#) Simplify management of threads for callbacks performed by database change - notification and eliminate a crash that occurred under high load in - certain situations. Thanks to Calvin S. for noting the issue and suggesting - a solution and testing the patch. -#) Force server detach on close so that the connection is completely closed - and not just the session as before. -#) Force use of OCI_UTF16ID for NCLOBs as using the default character set - would result in ORA-03127 with Oracle 11.2.0.2 and UTF8 character set. -#) Avoid attempting to clear temporary LOBs a second time when destroying the - variable as in certain situations this results in spurious errors. -#) Added additional parameter service_name to makedsn() which can be used to - use the service_name rather than the SID in the DSN string that is - generated. -#) Fix cursor description in test suite to take into account the number of - bytes per character. -#) Added tests for NCLOBS to the test suite. -#) Removed redundant code in setup.py for calculating the library path. - - -Version 5.1 (March 2011) ------------------------- - -#) Remove support for UNICODE mode and permit Unicode to be passed through in - everywhere a string may be passed in. This means that strings will be - passed through to Oracle using the value of the NLS_LANG environment - variable in Python 3.x as well. Doing this eliminated a bunch of problems - that were discovered by using UNICODE mode and also removed an unnecessary - restriction in Python 2.x that Unicode could not be used in connect strings - or SQL statements, for example. -#) Added support for creating an empty object variable via a named type, the - first step to adding full object support. -#) Added support for Python 3.2. -#) Account for lib64 used on x86_64 systems. Thanks to Alex Wood for supplying - the patch. -#) Clear up potential problems when calling cursor.close() ahead of the - cursor being freed by going out of scope. -#) Avoid compilation difficulties on AIX5 as OCIPing does not appear to be - available on that platform under Oracle 10g Release 2. Thanks to - Pierre-Yves Fontaniere for the patch. -#) Free temporary LOBs prior to each fetch in order to avoid leaking them. - Thanks to Uwe Hoffmann for the initial patch. - - -Version 5.0.4 (July 2010) -------------------------- - -#) Added support for Python 2.7. -#) Added support for new parameter (port) for subscription() call which allows - the client to specify the listening port for callback notifications from - the database server. Thanks to Geoffrey Weber for the initial patch. -#) Fixed compilation under Oracle 9i. -#) Fixed a few error messages. - - -Version 5.0.3 (February 2010) ------------------------------ - -#) Added support for 64-bit Windows. -#) Added support for Python 3.1 and dropped support for Python 3.0. -#) Added support for keyword parameters in cursor.callproc() and - cursor.callfunc(). -#) Added documentation for the UNICODE and FIXED_UNICODE variable types. -#) Added extra link arguments required for Mac OS X as suggested by Jason - Woodward. -#) Added additional error codes to the list of error codes that raise - OperationalError rather than DatabaseError. -#) Fixed calculation of display size for strings with national database - character sets that are not the default AL16UTF16. -#) Moved the resetting of the setinputsizes flag before the binding takes - place so that if an error takes place and a new statement is prepared - subsequently, spurious errors will not occur. -#) Fixed compilation with Oracle 10g Release 1. -#) Tweaked documentation based on feedback from a number of people. -#) Added support for running the test suite using "python setup.py test" -#) Added support for setting the CLIENT_IDENTIFIER value in the v$session - table for connections. -#) Added exception when attempting to call executemany() with arrays which is - not supported by the OCI. -#) Fixed bug when converting from decimal would result in OCI-22062 because - the locale decimal point was not a period. Thanks to Amaury Forgeot d'Arc - for the solution to this problem. - - -Version 5.0.2 (May 2009) ------------------------- - -#) Fix creation of temporary NCLOB values and the writing of NCLOB values in - non Unicode mode. -#) Re-enabled parsing of non select statements as requested by Roy Terrill. -#) Implemented a parse error offset as requested by Catherine Devlin. -#) Removed lib subdirectory when forcing RPATH now that the library directory - is being calculated exactly in setup.py. -#) Added an additional cast in order to support compiling by Microsoft - Visual C++ 2008 as requested by Marco de Paoli. -#) Added additional include directory to setup.py in order to support - compiling by Microsoft Visual Studio was requested by Jason Coombs. -#) Fixed a few documentation issues. - - -Version 5.0.1 (February 2009) ------------------------------ - -#) Added support for database change notification available in Oracle 10g - Release 2 and higher. -#) Fix bug where NCLOB data would be corrupted upon retrieval (non Unicode - mode) or would generate exception ORA-24806 (LOB form mismatch). Oracle - insists upon differentiating between CLOB and NCLOB no matter which - character set is being used for retrieval. -#) Add new attributes size, bufferSize and numElements to variable objects, - deprecating allocelems (replaced by numElements) and maxlength (replaced - by bufferSize) -#) Avoid increasing memory allocation for strings when using variable width - character sets and increasing the number of elements in a variable during - executemany(). -#) Tweaked code in order to ensure that cx_Oracle can compile with Python - 3.0.1. - - -Version 5.0 (December 2008) ---------------------------- - -#) Added support for Python 3.0 with much help from Amaury Forgeot d'Arc. -#) Removed support for Python 2.3 and Oracle 8i. -#) Added support for full unicode mode in Python 2.x where all strings are - passed in and returned as unicode (module must be built in this mode) - rather than encoded strings -#) nchar and nvarchar columns now return unicode instead of encoded strings -#) Added support for an output type handler and/or an input type handler to be - specified at the connection and cursor levels. -#) Added support for specifying both input and output converters for variables -#) Added support for specifying the array size of variables that are created - using the cursor.var() method -#) Added support for events mode and database resident connection pooling - (DRCP) in Oracle 11g. -#) Added support for changing the password during construction of a new - connection object as well as after the connection object has been created -#) Added support for the interval day to second data type in Oracle, - represented as datetime.timedelta objects in Python. -#) Added support for getting and setting the current_schema attribute for a - session -#) Added support for proxy authentication in session pools as requested by - Michael Wegrzynek (and thanks for the initial patch as well). -#) Modified connection.prepare() to return a boolean indicating if a - transaction was actually prepared in order to avoid the error ORA-24756 - (transaction does not exist). -#) Raise a cx_Oracle.Error instance rather than a string for column - truncation errors as requested by Helge Tesdal. -#) Fixed handling of environment handles in session pools in order to allow - session pools to fetch objects without exceptions taking place. - - -Version 4.4.1 (October 2008) ----------------------------- - -#) Make the bind variables and fetch variables accessible although they need - to be treated carefully since they are used internally; support added for - forward compatibility with version 5.x. -#) Include the "cannot insert null value" in the list of errors that are - treated as integrity errors as requested by Matt Boersma. -#) Use a cx_Oracle.Error instance rather than a string to hold the error when - truncation (ORA-1406) takes place as requested by Helge Tesdal. -#) Added support for fixed char, old style varchar and timestamp attribute - values in objects. -#) Tweaked setup.py to check for the Oracle version up front rather than - during the build in order to produce more meaningful errors and simplify - the code. -#) In setup.py added proper detection for the instant client on Mac OS X as - recommended by Martijn Pieters. -#) In setup.py, avoided resetting the extraLinkArgs on Mac OS X as doing so - prevents simple modification where desired as expressed by Christian - Zagrodnick. -#) Added documentation on exception handling as requested by Andreas Mock, who - also graciously provided an initial patch. -#) Modified documentation indicating that the password attribute on connection - objects can be written. -#) Added documentation warning that parameters not passed in during subsequent - executions of a statement will retain their original values as requested by - Harald Armin Massa. -#) Added comments indicating that an Oracle client is required since so many - people find this surprising. -#) Removed all references to Oracle 8i from the documentation and version 5.x - will eliminate all vestiges of support for this version of the Oracle - client. -#) Added additional link arguments for Cygwin as requested by Rob Gillen. - - -Version 4.4 (June 2008) ------------------------ - -#) Fix setup.py to handle the Oracle instant client and Oracle XE on both - Linux and Windows as pointed out by many. Thanks also to the many people - who also provided patches. -#) Set the default array size to 50 instead of 1 as the DB API suggests - because the performance difference is so drastic and many people have - recommended that the default be changed. -#) Added Py_BEGIN_ALLOW_THREADS and Py_END_ALLOW_THREADS around each blocking - call for LOBs as requested by Jason Conroy who also provided an initial - patch and performed a number of tests that demonstrate the new code is much - more responsive. -#) Add support for acquiring cursor.description after a parse. -#) Defer type assignment when performing executemany() until the last possible - moment if the value being bound in is null as suggested by Dragos Dociu. -#) When dropping a connection from the pool, ignore any errors that occur - during the rollback; unfortunately, Oracle decides to commit data even when - dropping a connection from the pool instead of rolling it back so the - attempt still has to be made. -#) Added support for setting CLIENT_DRIVER in V$SESSION_CONNECT_INFO in Oracle - 11g and higher. -#) Use cx_Oracle.InterfaceError rather than the builtin RuntimeError when - unable to create the Oracle environment object as requested by Luke Mewburn - since the error is specific to Oracle and someone attempting to catch any - exception cannot simply use cx_Oracle.Error. -#) Translated some error codes to OperationalError as requested by Matthew - Harriger; translated if/elseif/else logic to switch statement to make it - more readable and to allow for additional translation if desired. -#) Transformed documentation to new format using restructured text. Thanks to - Waldemar Osuch for contributing the initial draft of the new documentation. -#) Allow the password to be overwritten by a new value as requested by Alex - VanderWoude; this value is retained as a convenience to the user and not - used by anything in the module; if changed externally it may be convenient - to keep this copy up to date. -#) Cygwin is on Windows so should be treated in the same way as noted by - Matthew Cahn. -#) Add support for using setuptools if so desired as requested by Shreya - Bhatt. -#) Specify that the version of Oracle 10 that is now primarily used is 10.2, - not 10.1. - - -Version 4.3.3 (October 2007) ----------------------------- - -#) Added method ping() on connections which can be used to test whether or not - a connection is still active (available in Oracle 10g R2). -#) Added method cx_Oracle.clientversion() which returns a 5-tuple giving the - version of the client that is in use (available in Oracle 10g R2). -#) Added methods startup() and shutdown() on connections which can be used to - startup and shutdown databases (available in Oracle 10g R2). -#) Added support for Oracle 11g. -#) Added samples directory which contains a handful of scripts containing - sample code for more advanced techniques. More will follow in future - releases. -#) Prevent error "ORA-24333: zero iteration count" when calling executemany() - with zero rows as requested by Andreas Mock. -#) Added methods __enter__() and __exit__() on connections to support using - connections as context managers in Python 2.5 and higher. The context - managed is the transaction state. Upon exit the transaction is either - rolled back or committed depending on whether an exception took place or - not. -#) Make the search for the lib32 and lib64 directories automatic for all - platforms. -#) Tweak the setup configuration script to include all of the metadata and - allow for building the module within another setup configuration script -#) Include the Oracle version in addition to the Python version in the build - directories that are created and in the names of the binary packages that - are created. -#) Remove unnecessary dependency on win32api to build module on Windows. - - -Version 4.3.2 (August 2007) ---------------------------- - -#) Added methods open(), close(), isopen() and getchunksize() in order to - improve performance of reading/writing LOB values in chunks. -#) Fixed support for native doubles and floats in Oracle 10g; added new type - NATIVE_FLOAT to allow specification of a variable of that specific type - where desired. Thanks to D.R. Boxhoorn for pointing out the fact that this - was not working properly when the arraysize was anything other than 1. -#) When calling connection.begin(), only create a new transaction handle if - one is not already associated with the connection. Thanks to Andreas Mock - for discovering this and for Amaury Forgeot d'Arc for diagnosing the - problem and pointing the way to a solution. -#) Added attribute cursor.rowfactory which allows a method to be called for - each row that is returned; this is about 20% faster than calling the method - in Python using the idiom [method(\*r) for r in cursor]. -#) Attempt to locate an Oracle installation by looking at the PATH if the - environment variable ORACLE_HOME is not set; this is of primary use on - Windows where this variable should not normally be set. -#) Added support for autocommit mode as requested by Ian Kelly. -#) Added support for connection.stmtcachesize which allows for both reading - and writing the size of the statement cache size. This parameter can make a - huge difference with the length of time taken to prepare statements. Added - support for setting the statement tag when preparing a statement. Both of - these were requested by Bjorn Sandberg who also provided an initial patch. -#) When copying the value of a variable, copy the return code as well. - - -Version 4.3.1 (April 2007) --------------------------- - -#) Ensure that if the client buffer size exceeds 4000 bytes that the server - buffer size does not as strings may only contain 4000 bytes; this allows - handling of multibyte character sets on the server as well as the client. -#) Added support for using buffer objects to populate binary data and made the - Binary() constructor the buffer type as requested by Ken Mason. -#) Fix potential crash when using full optimization with some compilers. - Thanks to Aris Motas for noticing this and providing the initial patch and - to Amaury Forgeot d'Arc for providing an even simpler solution. -#) Pass the correct charset form in to the write call in order to support - writing to national character set LOB values properly. Thanks to Ian Kelly - for noticing this discrepancy. - - -Version 4.3 (March 2007) ------------------------- - -#) Added preliminary support for fetching Oracle objects (SQL types) as - requested by Kristof Beyls (who kindly provided an initial patch). - Additional work needs to be done to support binding and updating objects - but the basic structure is now in place. -#) Added connection.maxBytesPerCharacter which indicates the maximum number of - bytes each character can use; use this value to also determine the size of - local buffers in order to handle discrepancies between the client character - set and the server character set. Thanks to Andreas Mock for providing the - initial patch and working with me to resolve this issue. -#) Added support for querying native floats in Oracle 10g as requested by - Danny Boxhoorn. -#) Add support for temporary LOB variables created via PL/SQL instead of only - directly by cx_Oracle; thanks to Henning von Bargen for discovering this - problem. -#) Added support for specifying variable types using the builtin types int, - float, str and datetime.date which allows for finer control of what type of - Python object is returned from cursor.callfunc() for example. -#) Added support for passing booleans to callproc() and callfunc() as - requested by Anana Aiyer. -#) Fixed support for 64-bit environments in Python 2.5. -#) Thanks to Filip Ballegeer and a number of his co-workers, an intermittent - crash was tracked down; specifically, if a connection is closed, then the - call to OCIStmtRelease() will free memory twice. Preventing the call when - the connection is closed solves the problem. - - -Version 4.2.1 (September 2006) ------------------------------- - -#) Added additional type (NCLOB) to handle CLOBs that use the national - character set as requested by Chris Dunscombe. -#) Added support for returning cursors from functions as requested by Daniel - Steinmann. -#) Added support for getting/setting the "get" mode on session pools as - requested by Anand Aiyer. -#) Added support for binding subclassed cursors. -#) Fixed binding of decimal objects with absolute values less than 0.1. - - -Version 4.2 (July 2006) ------------------------ - -#) Added support for parsing an Oracle statement as requested by Patrick - Blackwill. -#) Added support for BFILEs at the request of Matthew Cahn. -#) Added support for binding decimal.Decimal objects to cursors. -#) Added support for reading from NCLOBs as requested by Chris Dunscombe. -#) Added connection attributes encoding and nencoding which return the IANA - character set name for the character set and national character set in use - by the client. -#) Rework module initialization to use the techniques recommended by the - Python documentation as one user was experiencing random segfaults due - to the use of the module dictionary after the initialization was complete. -#) Removed support for the OPT_Threading attribute. Use the threaded keyword - when creating connections and session pools instead. -#) Removed support for the OPT_NumbersAsStrings attribute. Use the - numbersAsStrings attribute on cursors instead. -#) Use type long rather than type int in order to support long integers on - 64-bit machines as reported by Uwe Hoffmann. -#) Add cursor attribute "bindarraysize" which is defaulted to 1 and is used - to determine the size of the arrays created for bind variables. -#) Added repr() methods to provide something a little more useful than the - standard type name and memory address. -#) Added keyword parameter support to the functions that imply such in the - documentation as requested by Harald Armin Massa. -#) Treat an empty dictionary passed through to cursor.execute() as keyword - parameters the same as if no keyword parameters were specified at all, as - requested by Fabien Grumelard. -#) Fixed memory leak when a LOB read would fail. -#) Set the LDFLAGS value in the environment rather than directly in the - setup.py file in order to satisfy those who wish to enable the use of - debugging symbols. -#) Use __DATE__ and __TIME__ to determine the date and time of the build - rather than passing it through directly. -#) Use Oracle types and add casts to reduce warnings as requested by Amaury - Forgeot d'Arc. -#) Fixed typo in error message. - - -Version 4.1.2 (December 2005) ------------------------------ - -#) Restore support of Oracle 9i features when using the Oracle 10g client. - - -Version 4.1.1 (December 2005) ------------------------------ - -#) Add support for dropping a connection from a session pool. -#) Add support for write only attributes "module", "action" and "clientinfo" - which work only in Oracle 10g as requested by Egor Starostin. -#) Add support for pickling database errors. -#) Use the previously created bind variable as a template if available when - creating a new variable of a larger size. Thanks to Ted Skolnick for the - initial patch. -#) Fixed tests to work properly in the Python 2.4 environment where dates and - timestamps are different Python types. Thanks to Henning von Bargen for - pointing this out. -#) Added additional directories to search for include files and libraries in - order to better support the Oracle 10g instant client. -#) Set the internal fetch number to 0 in order to satisfy very picky source - analysis tools as requested by Amaury Fogeot d'Arc. -#) Improve the documentation for building and installing the module from - source as some people are unaware of the standard methods for building - Python modules using distutils. -#) Added note in the documentation indicating that the arraysize attribute - can drastically affect performance of queries since this seems to be a - common misunderstanding of first time users of cx_Oracle. -#) Add a comment indicating that on HP-UX Itanium with Oracle 10g the library - ttsh10 must also be linked against. Thanks to Bernard Delmee for the - information. - - -Version 4.1 (January 2005) --------------------------- - -#) Fixed bug where subclasses of Cursor do not pass the connection in the - constructor causing a segfault. -#) DDL statements must be reparsed before execution as noted by Mihai - Ibanescu. -#) Add support for setting input sizes by position. -#) Fixed problem with catching an exception during execute and then still - attempting to perform a fetch afterwards as noted by Leith Parkin. -#) Rename the types so that they can be pickled and unpickled. Thanks to Harri - Pasanen for pointing out the problem. -#) Handle invalid NLS_LANG setting properly (Oracle seems to like to provide a - handle back even though it is invalid) and determine the number of bytes - per character in order to allow for proper support in the future of - multibyte and variable width character sets. -#) Remove date checking from the native case since Python already checks that - dates are valid; enhance error message when invalid dates are encountered - so that additional processing can be done. -#) Fix bug executing SQL using numeric parameter names with predefined - variables (such as what takes place when calling stored procedures with out - parameters). -#) Add support for reading CLOB values using multibyte or variable length - character sets. - - -Version 4.1 beta 1 (September 2004) ------------------------------------ - -#) Added support for Python 2.4. In Python 2.4, the datetime module is used - for both binding and fetching of date and timestamp data. In Python 2.3, - objects from the datetime module can be bound but the internal datetime - objects will be returned from queries. -#) Added pickling support for LOB and datetime data. -#) Fully qualified the table name that was missing in an alter table - statement in the setup test script as noted by Marc Gehling. -#) Added a section allowing for the setting of the RPATH linker directive in - setup.py as requested by Iustin Pop. -#) Added code to raise a programming error exception when an attempt is made - to access a LOB locator variable in a subsequent fetch. -#) The username, password and dsn (tnsentry) are stored on the connection - object when specified, regardless of whether or not a standard connection - takes place. -#) Added additional module level constant called "LOB" as requested by Joseph - Canedo. -#) Changed exception type to IntegrityError for constraint violations as - requested by Joseph Canedo. -#) If scale and precision are not specified, an attempt is made to return a - long integer as requested by Joseph Canedo. -#) Added workaround for Oracle bug which returns an invalid handle when the - prepare call fails. Thanks to alantam@hsbc.com for providing the code that - demonstrated the problem. -#) The cursor method arrayvar() will now accept the actual list so that it is - not necessary to call cursor.arrayvar() followed immediately by - var.setvalue(). -#) Fixed bug where attempts to execute the statement "None" with bind - variables would cause a segmentation fault. -#) Added support for binding by position (paramstyle = "numeric"). -#) Removed memory leak created by calls to OCIParamGet() which were not - mirrored by calls to OCIDescriptorFree(). Thanks to Mihai Ibanescu for - pointing this out and providing a patch. -#) Added support for calling cursor.executemany() with statement None - implying that the previously prepared statement ought to be executed. - Thanks to Mihai Ibanescu for providing a patch. -#) Added support for rebinding variables when a subsequent call to - cursor.executemany() uses a different number of rows. Thanks to Mihai - Ibanescu for supplying a patch. -#) The microseconds are now displayed in datetime variables when nonzero - similar to method used in the datetime module. -#) Added support for binary_float and binary_double columns in Oracle 10g. - - -Version 4.0.1 (February 2004) ------------------------------ - -#) Fixed bugs on 64-bit platforms that caused segmentation faults and bus - errors in session pooling and determining the bind variables associated - with a statement. -#) Modified test suite so that 64-bit platforms are tested properly. -#) Added missing commit statements in the test setup scripts. Thanks to Keith - Lyon for pointing this out. -#) Fix setup.py for Cygwin environments. Thanks to Doug Henderson for - providing the necessary fix. -#) Added support for compiling cx_Oracle without thread support. Thanks to - Andre Reitz for pointing this out. -#) Added support for a new keyword parameter called threaded on connections - and session pools. This parameter defaults to False and indicates whether - threaded mode ought to be used. It replaces the module level attribute - OPT_Threading although examining the attribute will be retained until the - next release at least. -#) Added support for a new keyword parameter called twophase on connections. - This parameter defaults to False and indicates whether support for two - phase (distributed or global) transactions ought to be present. Note that - support for distributed transactions is buggy when crossing major version - boundaries (Oracle 8i to Oracle 9i for example). -#) Ensure that the rowcount attribute is set properly when an exception is - raised during execution. Thanks to Gary Aviv for pointing out this problem - and its solution. - - -Version 4.0 (December 2003) ---------------------------- - -#) Added support for subclassing connections, cursors and session pools. The - changes involved made it necessary to drop support for Python 2.1 and - earlier although a branch exists in CVS to allow for support of Python 2.1 - and earlier if needed. -#) Connections and session pools can now be created with keyword parameters, - not just sequential parameters. -#) Queries now return integers whenever possible and long integers if the - number will overflow a simple integer. Floats are only returned when it is - known that the number is a floating point number or the integer conversion - fails. -#) Added initial support for user callbacks on OCI functions. See the - documentation for more details. -#) Add support for retrieving the bind variable names associated with a - cursor with a new method bindnames(). -#) Add support for temporary LOB variables. This means that setinputsizes() - can be used with the values CLOB and BLOB to create these temporary LOB - variables and allow for the equivalent of empty_clob() and empty_blob() - since otherwise Oracle will treat empty strings as NULL values. -#) Automatically switch to long strings when the data size exceeds the - maximum string size that Oracle allows (4000 characters) and raise an - error if an attempt is made to set a string variable to a size that it - does not support. This avoids truncation errors as reported by Jon Franz. -#) Add support for global (distributed) transactions and two phase commit. -#) Force the NLS settings for the session so that test tables are populated - correctly in all circumstances; problems were noted by Ralf Braun and - Allan Poulsen. -#) Display error messages using the environment handle when the error handle - has not yet been created; this provides better error messages during this - rather rare situation. -#) Removed memory leak in callproc() that was reported by Todd Whiteman. -#) Make consistent the calls to manipulate memory; otherwise segfaults can - occur when the pymalloc option is used, as reported by Matt Hoskins. -#) Force a rollback when a session is released back to the session pool. - Apparently the connections are not as stateless as Oracle's documentation - suggests and this makes the logic consistent with normal connections. -#) Removed module method attach(). This can be replaced with a call to - Connection(handle=) if needed. - - -Version 3.1 (August 2003) -------------------------- - -#) Added support for connecting with SYSDBA and SYSOPER access which is - needed for connecting as sys in Oracle 9i. -#) Only check the dictionary size if the variable is not NULL; otherwise, an - error takes place which is not caught or cleared; this eliminates a - spurious "Objects/dictobject.c:1258: bad argument to internal function" in - Python 2.3. -#) Add support for session pooling. This is only support for Oracle 9i but - is amazingly fast -- about 100 times faster than connecting. -#) Add support for statement caching when pooling sessions, this reduces the - parse time considerably. Unfortunately, the Oracle OCI does not allow this - to be easily turned on for normal sessions. -#) Add method trim() on CLOB and BLOB variables for trimming the size. -#) Add support for externally identified users; to use this feature leave the - username and password fields empty when connecting. -#) Add method cancel() on connection objects to cancel long running queries. - Note that this only works on non-Windows platforms. -#) Add method callfunc() on cursor objects to allow calling a function - without using an anonymous PL/SQL block. -#) Added documentation on objects that were not documented. At this point all - objects, methods and constants in cx_Oracle have been documented. -#) Added support for timestamp columns in Oracle 9i. -#) Added module level method makedsn() which creates a data source name given - the host, port and SID. -#) Added constant "buildtime" which is the time when the module was built as - an additional means of identifying the build that is in use. -#) Binding a value that is incompatible to the previous value that was bound - (data types do not match or array size is larger) will now result in a - new bind taking place. This is more consistent with the DB API although - it does imply a performance penalty when used. - - -Version 3.0a (June 2003) ------------------------- - -#) Fixed bug where zero length PL/SQL arrays were being mishandled -#) Fixed support for the data type "float" in Oracle; added one to the - display size to allow for the sign of the number, if necessary; changed - the display size of unconstrained numbers to 127, which is the largest - number that Oracle can handle -#) Added support for retrieving the description of a bound cursor before - fetching it -#) Fixed a couple of build issues on Mac OS X, AIX and Solaris (64-bit) -#) Modified documentation slightly based on comments from several people -#) Included files in MANIFEST that are needed to generate the binaries -#) Modified test suite to work within the test environment at Computronix - as well as within the packages that are distributed - - -Version 3.0 (March 2003) ------------------------- - -#) Removed support for connection to Oracle7 databases; it is entirely - possible that it will still work but I no longer have any way of testing - and Oracle has dropped any meaningful support for Oracle7 anyway -#) Fetching of strings is now done with predefined memory areas rather than - dynamic memory areas; dynamic fetching of strings was causing problems - with Oracle 9i in some instances and databases using a different character - set other than US ASCII -#) Fixed bug where segfault would occur if the '/' character preceded the '@' - character in a connect string -#) Added two new cursor methods var() and arrayvar() in order to eliminate - the need for setinputsizes() when defining PL/SQL arrays and as a generic - method of acquiring bind variables directly when needed -#) Fixed support for binding cursors and added support for fetching cursors - (these are known as ref cursors in PL/SQL). -#) Eliminated discrepancy between the array size used internally and the - array size specified by the interface user; this was done earlier to avoid - bus errors on 64-bit platforms but another way has been found to get - around that issue and a number of people were getting confused because of - the discrepancy -#) Added support for the attribute "connection" on cursors, an optional - DB API extension -#) Added support for passing a dictionary as the second parameter for the - cursor.execute() method in order to comply with the DB API more closely; - the method of passing parameters with keyword parameters is still supported - and is in fact preferred -#) Added support for the attribute "statement" on cursors which is a - reference to the last SQL statement prepared or executed -#) Added support for passing any sequence to callproc() rather than just - lists as before -#) Fixed bug where segfault would occur if the array size was changed after - the cursor was executed but before it was fetched -#) Ignore array size when performing executemany() and use the length of the - list of parameters instead -#) Rollback when connection is closed or destroyed to follow DB API rather - than use the Oracle default (which is commit) -#) Added check for array size too large causing an integer overflow -#) Added support for iterators for Python 2.2 and above -#) Added test suite based on PyUnitTest -#) Added documentation in HTML format similar to the documentation for the - core Python library - - -Version 2.5a (August 2002) --------------------------- - -#) Fix problem with Oracle 9i and retrieving strings; it seems that Oracle 9i - uses the correct method for dynamic callback but Oracle 8i will not work - with that method so an #ifdef was added to check for the existence of an - Oracle 9i feature; thanks to Paul Denize for discovering this problem - - -Version 2.5 (July 2002) ------------------------ - -#) Added flag OPT_NoOracle7 which, if set, assumes that connections are being - made to Oracle8 or higher databases; this allows for eliminating the - overhead in performing this check at connect time -#) Added flag OPT_NumbersAsStrings which, if set, returns all numbers as - strings rather than integers or floats; this flag is used when defined - variables are created (during select statements only) -#) Added flag OPT_Threading which, if set, uses OCI threading mode; there is a - significant performance degradation in this mode (about 15-20%) but it does - allow threads to share connections (threadsafety level 2 according to the - Python Database API 2.0); note that in order to support this, Oracle 8i or - higher is now required -#) Added Py_BEGIN_ALLOW_THREADS and Py_END_ALLOW_THREADS pairs where - applicable to support threading during blocking OCI calls -#) Added global method attach() to cx_Oracle to support attaching to an - existing database handle (as provided by PowerBuilder, for example) -#) Eliminated the cursor method fetchbinds() which was used for returning the - list of bind variables after execution to get the values of out variables; - the cursor method setinputsizes() was modified to return the list of bind - variables and the cursor method execute() was modified to return the list - of defined variables in the case of a select statement being executed; - these variables have three methods available to them: getvalue([]) to - get the value of a variable, setvalue(, ) to set its value and - copy(, , ) to copy the value from a variable in a - more efficient manner than setvalue(getvalue()) -#) Implemented cursor method executemany() which expects a list of - dictionaries for the parameters -#) Implemented cursor method callproc() -#) Added cursor method prepare() which parses (prepares) the statement for - execution; subsequent execute() or executemany() calls can pass None as the - statement which will imply use of the previously prepared statement; used - for high performance only -#) Added cursor method fetchraw() which will perform a raw fetch of the cursor - returning the number of rows thus fetched; this is used to avoid the - overhead of generating result sets; used for high performance only -#) Added cursor method executemanyprepared() which is identical to the method - executemany() except that it takes a single parameter which is the number - of times to execute a previously prepared statement and it assumes that the - bind variables already have their values set; used for high performance - only -#) Added support for rowid being returned in a select statement -#) Added support for comparing dates returned by cx_Oracle -#) Integrated patch from Andre Reitz to set the null ok flag in the - description attribute of the cursor -#) Integrated patch from Andre Reitz to setup.py to support compilation with - Python 1.5 -#) Integrated patch from Benjamin Kearns to setup.py to support compilation - on Cygwin - - -Version 2.4 (January 2002) --------------------------- - -#) String variables can now be made any length (previously restricted to the - 64K limit imposed by Oracle for default binding); use the type - cx_Oracle.LONG_STRING as the parameter to setinputsizes() for binding in - string values larger than 4000 bytes. -#) Raw and long raw columns are now supported; use the types cx_Oracle.BINARY - and cx_Oracle.LONG_BINARY as the parameter to setinputsizes() for binding - in values of these types. -#) Functions DateFromTicks(), TimeFromTicks() and TimestampFromTicks() - are now implemented. -#) Function cursor.setoutputsize() implemented -#) Added the ability to bind arrays as out parameters to procedures; use the - format [cx_Oracle., ] as the input to the function - setinputsizes() for binding arrays -#) Discovered from the Oracle 8.1.6 version of the documentation of the OCI - libraries, that the size of the memory location required for the precision - variable is larger than the printed documentation says; this was causing a - problem with the code on the Sun platform. -#) Now support building RPMs for Linux. - - -Version 2.3 (October 2001) --------------------------- - -#) Incremental performance enhancements (dealing with reusing cursors and - bind handles) -#) Ensured that arrays of integers with a single float in them are all - treated as floats, as suggested by Martin Koch. -#) Fixed code dealing with scale and precision for both defining a numeric - variable and for providing the cursor description; this eliminates the - problem of an underflow error (OCI-22054) when retrieving data with - non-zero scale. - - -Version 2.2 (July 2001) ------------------------ - -#) Upgraded thread safety to level 1 (according to the Python DB API 2.0) as - an internal project required the ability to share the module between - threads. -#) Added ability to bind ref cursors to PL/SQL blocks as requested by - Brad Powell. -#) Added function write(Value, [Offset]) to LOB variables as requested by - Matthias Kirst. -#) Procedure execute() on Cursor objects now permits a value None for the - statement which means that the previously prepared statement will be - executed and any input sizes set earlier will be retained. This was done to - improve the performance of scripts that execute one statement many times. -#) Modified module global constants BINARY and DATETIME to point to the - external representations of those types so that the expression - type(var) == cx_Oracle.DATETIME will work as expected. -#) Added global constant version to provide means of determining the current - version of the module. -#) Modified error checking routine to distinguish between an Oracle error and - invalid handles. -#) Added error checking to avoid setting the value of a bind variable to a - value that it cannot support and raised an exception to indicate this fact. -#) Added extra compile arguments for the AIX platform as suggested by Jehwan - Ryu. -#) Added section to the README to indicate the method for a binary - installation as suggested by Steve Holden. -#) Added simple usage example as requested by many people. -#) Added HISTORY file to the distribution. +See `python-oracledb Release Notes `__. diff --git a/doc/src/user_guide/aq.rst b/doc/src/user_guide/aq.rst index 2214e783..9c6554bc 100644 --- a/doc/src/user_guide/aq.rst +++ b/doc/src/user_guide/aq.rst @@ -4,208 +4,8 @@ Oracle Advanced Queuing (AQ) **************************** -`Oracle Advanced Queuing -`__ 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. +.. include:: ../note.rst -cx_Oracle 7.2 introduced an updated interface for Oracle Advanced -Queuing. - -There are Advanced Queuing examples in the `GitHub examples -`__ 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 `. 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 ` and -`Oracle Advanced Queuing documentation -`__ 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. +See `Using Oracle Transactional Event Queues and Advanced Queuing +`__ in +the python-oracledb documentation. diff --git a/doc/src/user_guide/batch_statement.rst b/doc/src/user_guide/batch_statement.rst index 716c93f2..58b8da2a 100644 --- a/doc/src/user_guide/batch_statement.rst +++ b/doc/src/user_guide/batch_statement.rst @@ -4,291 +4,8 @@ Batch Statement Execution and Bulk Loading ****************************************** -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. +.. include:: ../note.rst -There are examples in the `GitHub examples -`__ -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 ` 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 -`__ 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 -`__ 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. +See `Executing Batch Statements and Bulk Loading `__ in the +python-oracledb documentation. diff --git a/doc/src/user_guide/bind.rst b/doc/src/user_guide/bind.rst index 69d86cec..46242230 100644 --- a/doc/src/user_guide/bind.rst +++ b/doc/src/user_guide/bind.rst @@ -4,802 +4,7 @@ Using Bind Variables ******************** -SQL and PL/SQL statements that pass data to and from Oracle Database should use -placeholders in SQL and PL/SQL statements that mark where data is supplied or -returned. 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: +.. include:: ../note.rst -.. 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 -`__ 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 `. - - -.. _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 ( ) - -The best way to do the '' 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``. +See `Using Bind Variables `__ in the python-oracledb documentation. diff --git a/doc/src/user_guide/connection_handling.rst b/doc/src/user_guide/connection_handling.rst index e80e4dba..deed26da 100644 --- a/doc/src/user_guide/connection_handling.rst +++ b/doc/src/user_guide/connection_handling.rst @@ -4,1589 +4,8 @@ Connecting to Oracle Database ***************************** -Connections between cx_Oracle and Oracle Database are used for executing -:ref:`SQL `, :ref:`PL/SQL `, and :ref:`SODA -`. +.. include:: ../note.rst -Establishing Database Connections -================================= - -There are two ways to connect to Oracle Database using cx_Oracle: - -* **Standalone connections** - - These are useful when the application maintains a single user - session to a database. Connections are created by - :meth:`cx_Oracle.connect()` or its alias - :meth:`cx_Oracle.Connection()`. - -* **Pooled connections** - - :ref:`Connection pooling ` is important for performance when - applications frequently connect and disconnect from the database. Pools - support Oracle's :ref:`high availability ` features and are - recommended for applications that must be reliable. Small pools can also be - useful for applications that want a few connections available for infrequent - use. Pools are created with :meth:`cx_Oracle.SessionPool()` at application - initialization time, and then :meth:`SessionPool.acquire()` can be called to - obtain a connection from a pool. - -Many connection behaviors can be controlled by cx_Oracle options. Other -settings can be configured in :ref:`optnetfiles` or in :ref:`optclientfiles`. -These include limiting the amount of time that opening a connection can take, or -enabling :ref:`network encryption `. - -**Example: Standalone Connection to Oracle Database** - -.. code-block:: python - - import cx_Oracle - - userpwd = ". . ." # Obtain password string from a user prompt or environment variable - - connection = cx_Oracle.connect(user="hr", password=userpwd, - dsn="dbhost.example.com/orclpdb1", - encoding="UTF-8") - -cx_Oracle also supports :ref:`external authentication ` so -passwords do not need to be in the application. - - -Closing Connections -=================== - -Connections should be released when they are no longer needed by calling -:meth:`Connection.close()`. Alternatively, you may prefer to let connections -be automatically cleaned up when references to them go out of scope. This lets -cx_Oracle close dependent resources in the correct order. One other approach is -the use of a "with" block, for example: - -.. code-block:: python - - with cx_Oracle.connect(user=user, password=password, - dsn="dbhost.example.com/orclpdb1", - encoding="UTF-8") as connection: - cursor = connection.cursor() - cursor.execute("insert into SomeTable values (:1, :2)", - (1, "Some string")) - connection.commit() - -This code ensures that, once the block is completed, the connection is closed -and resources have been reclaimed by the database. In addition, any attempt to -use the variable ``connection`` outside of the block will simply fail. - -Prompt closing of connections is important when using connection pools so -connections are available for reuse by other pool users. - -.. _connstr: - -Connection Strings -================== - -The data source name parameter ``dsn`` of :meth:`cx_Oracle.connect()` and -:meth:`cx_Oracle.SessionPool()` is the Oracle Database connection string -identifying which database service to connect to. The ``dsn`` string can be one -of: - -* An Oracle Easy Connect string -* An Oracle Net Connect Descriptor string -* A Net Service Name mapping to a connect descriptor - -For more information about naming methods, see `Oracle Net Service Reference `__. - -.. _easyconnect: - -Easy Connect Syntax for Connection Strings ------------------------------------------- - -An Easy Connect string is often the simplest connection string to use for the -data source name parameter ``dsn`` of :meth:`cx_Oracle.connect()` and -:meth:`cx_Oracle.SessionPool()`. This method does not need configuration files -such as ``tnsnames.ora``. - -For example, to connect to the Oracle Database service ``orclpdb1`` that is -running on the host ``dbhost.example.com`` with the default Oracle -Database port 1521, use: - -.. code-block:: python - - connection = cx_Oracle.connect(user="hr", password=userpwd, - dsn="dbhost.example.com/orclpdb1", - encoding="UTF-8") - -If the database is using a non-default port, it must be specified: - -.. code-block:: python - - connection = cx_Oracle.connect(user="hr", password=userpwd, - dsn="dbhost.example.com:1984/orclpdb1", - encoding="UTF-8") - -The Easy Connect syntax supports Oracle Database service names. It cannot be -used with the older System Identifiers (SID). - -The Easy Connect syntax has been extended in recent versions of Oracle Database -client since its introduction in 10g. Check the Easy Connect Naming method in -`Oracle Net Service Administrator's Guide -`__ for the syntax to use in your -version of the Oracle Client libraries. - -If you are using Oracle Client 19c, the latest `Easy Connect Plus -`__ syntax allows the use of -multiple hosts or ports, along with optional entries for the wallet location, -the distinguished name of the database server, and even lets some network -configuration options be set. This means that a :ref:`sqlnet.ora ` -file is not needed for some common connection scenarios. - -Oracle Net Connect Descriptor Strings -------------------------------------- - -The :meth:`cx_Oracle.makedsn()` function can be used to construct a connect -descriptor string for the data source name parameter ``dsn`` of -:meth:`cx_Oracle.connect()` and :meth:`cx_Oracle.SessionPool()`. The -:meth:`~cx_Oracle.makedsn()` function accepts the database hostname, the port -number, and the service name. It also supports :ref:`sharding ` -syntax. - -For example, to connect to the Oracle Database service ``orclpdb1`` that is -running on the host ``dbhost.example.com`` with the default Oracle -Database port 1521, use: - -.. code-block:: python - - dsn = cx_Oracle.makedsn("dbhost.example.com", 1521, service_name="orclpdb1") - connection = cx_Oracle.connect(user="hr", password=userpwd, dsn=dsn, - encoding="UTF-8") - -Note the use of the named argument ``service_name``. By default, the third -parameter of :meth:`~cx_Oracle.makedsn()` is a database System Identifier (SID), -not a service name. However, almost all current databases use service names. - -The value of ``dsn`` in this example is the connect descriptor string:: - - (DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=dbhost.example.com)(PORT=1521))(CONNECT_DATA=(SERVICE_NAME=orclpdb1))) - -You can manually create similar connect descriptor strings. This lets you -extend the syntax, for example to support failover. These strings can be -embedded directly in the application: - -.. code-block:: python - - dsn = """(DESCRIPTION= - (FAILOVER=on) - (ADDRESS_LIST= - (ADDRESS=(PROTOCOL=tcp)(HOST=sales1-svr)(PORT=1521)) - (ADDRESS=(PROTOCOL=tcp)(HOST=sales2-svr)(PORT=1521))) - (CONNECT_DATA=(SERVICE_NAME=sales.example.com)))""" - - connection = cx_Oracle.connect(user="hr", password=userpwd, dsn=dsn, - encoding="UTF-8") - -.. _netservice: - -Net Service Names for Connection Strings ----------------------------------------- - -Connect Descriptor Strings are commonly stored in a :ref:`tnsnames.ora -` file and associated with a Net Service Name. This name can be -used directly for the data source name parameter ``dsn`` of -:meth:`cx_Oracle.connect()` and :meth:`cx_Oracle.SessionPool()`. For example, -given a ``tnsnames.ora`` file with the following contents:: - - ORCLPDB1 = - (DESCRIPTION = - (ADDRESS = (PROTOCOL = TCP)(HOST = dbhost.example.com)(PORT = 1521)) - (CONNECT_DATA = - (SERVER = DEDICATED) - (SERVICE_NAME = orclpdb1) - ) - ) - -then you could connect using the following code: - -.. code-block:: python - - connection = cx_Oracle.connect(user="hr", password=userpwd, dsn="orclpdb1", - encoding="UTF-8") - -For more information about Net Service Names, see -`Database Net Services Reference -`__. - -JDBC and Oracle SQL Developer Connection Strings ------------------------------------------------- - -The cx_Oracle connection string syntax is different to Java JDBC and the common -Oracle SQL Developer syntax. If these JDBC connection strings reference a -service name like:: - - jdbc:oracle:thin:@hostname:port/service_name - -for example:: - - jdbc:oracle:thin:@dbhost.example.com:1521/orclpdb1 - -then use Oracle's Easy Connect syntax in cx_Oracle: - -.. code-block:: python - - connection = cx_Oracle.connect(user="hr", password=userpwd, - dsn="dbhost.example.com:1521/orclpdb1", - encoding="UTF-8") - -Alternatively, if a JDBC connection string uses an old-style Oracle SID "system -identifier", and the database does not have a service name:: - - jdbc:oracle:thin:@hostname:port:sid - -for example:: - - jdbc:oracle:thin:@dbhost.example.com:1521:orcl - -then a connect descriptor string from ``makedsn()`` can be used in the -application: - -.. code-block:: python - - dsn = cx_Oracle.makedsn("dbhost.example.com", 1521, sid="orcl") - connection = cx_Oracle.connect(user="hr", password=userpwd, dsn=dsn, - encoding="UTF-8") - -Alternatively, create a ``tnsnames.ora`` (see :ref:`optnetfiles`) entry, for -example:: - - finance = - (DESCRIPTION = - (ADDRESS = (PROTOCOL = TCP)(HOST = dbhost.example.com)(PORT = 1521)) - (CONNECT_DATA = - (SID = ORCL) - ) - ) - -This can be referenced in cx_Oracle: - -.. code-block:: python - - connection = cx_Oracle.connect(user="hr", password=userpwd, dsn="finance", - encoding="UTF-8") - -.. _connpool: - -Connection Pooling -================== - -cx_Oracle's connection pooling lets applications create and maintain a pool of -connections to the database. Connection pooling is important for performance -when applications frequently connect and disconnect from the database. The pool -implementation uses Oracle's `session pool technology -`__ which supports Oracle's -:ref:`high availability ` features and is recommended for -applications that must be reliable. This also means that small pools can be -useful for applications that want a few connections available for infrequent -use. - -A connection pool is created by calling :meth:`~cx_Oracle.SessionPool()`. This -is generally called during application initialization. The initial pool size -and the maximum pool size are provided at the time of pool creation. When the -pool needs to grow, new connections are created automatically. The pool can -shrink back to the minimum size when connections are no longer in use. For -pools created with :ref:`external authentication `, with -:ref:`homogeneous ` set to False, or when using :ref:`drcp`, then -the number of connections initially created is zero even if a larger value is -specified for ``min``. Also in these cases the pool increment is always 1, -regardless of the value of ``increment``. - -After a pool has been created, connections can be obtained from it by calling -:meth:`~SessionPool.acquire()`. These connections can be used in the same way -that standalone connections are used. - -Connections acquired from the pool should be released back to the pool using -:meth:`SessionPool.release()` or :meth:`Connection.close()` when they are no -longer required. Otherwise, they will be released back to the pool -automatically when all of the variables referencing the connection go out of -scope. This make connections available for other users of the pool. - -The session pool can be completely closed using :meth:`SessionPool.close()`. - -The example below shows how to connect to Oracle Database using a -connection pool: - -.. code-block:: python - - # Create the session pool - pool = cx_Oracle.SessionPool(user="hr", password=userpwd, - dsn="dbhost.example.com/orclpdb1", min=2, - max=5, increment=1, encoding="UTF-8") - - # Acquire a connection from the pool - connection = pool.acquire() - - # Use the pooled connection - cursor = connection.cursor() - for result in cursor.execute("select * from mytab"): - print(result) - - # Release the connection to the pool - pool.release(connection) - - # Close the pool - pool.close() - -Other :meth:`cx_Oracle.SessionPool()` options can be used at pool creation. -For example the ``getmode`` value can be set so that any ``aquire()`` call will -wait for a connection to become available if all are currently in use, for -example: - -.. code-block:: python - - # Create the session pool - pool = cx_Oracle.SessionPool(user="hr", password=userpwd, - dsn="dbhost.example.com/orclpdb1", min=2, - max=5, increment=1, - getmode=cx_Oracle.SPOOL_ATTRVAL_WAIT, - encoding="UTF-8") - -See `ConnectionPool.py -`__ -for an example. - -Before :meth:`SessionPool.acquire()` returns, cx_Oracle does a lightweight check -to see if the network transport for the selected connection is still open. If -it is not, then :meth:`~SessionPool.acquire()` will clean up the connection and -return a different one. This check will not detect cases such as where the -database session has been killed by the DBA, or reached a database resource -manager quota limit. To help in those cases, :meth:`~SessionPool.acquire()` -will also do a full :ref:`round-trip ` ping to the database when it -is about to return a connection that was unused in the pool for -:data:`SessionPool.ping_interval` seconds. If the ping fails, the connection -will be discarded and another one obtained before :meth:`~SessionPool.acquire()` -returns to the application. Because this full ping is time based, it won't -catch every failure. Also network timeouts and session kills may occur after -:meth:`~SessionPool.acquire()` and before :meth:`Cursor.execute()`. To handle -these cases, applications need to check for errors after each -:meth:`~Cursor.execute()` and make application-specific decisions about retrying -work if there was a connection failure. Oracle's :ref:`Application Continuity -` can do this automatically in some cases. Note both the -lightweight and full ping connection checks can mask performance-impacting -configuration issues, for example firewalls killing connections, so monitor the -connection rate in `AWR -`__ -for an unexpected value. You can explicitly initiate a full ping to check -connection liveness with :meth:`Connection.ping()` but overuse will impact -performance and scalability. - -Connection Pool Sizing ----------------------- - -The Oracle Real-World Performance Group's recommendation is to use fixed size -connection pools. The values of ``min`` and ``max`` should be the same (and the -``increment`` equal to zero). This avoids connection storms which can decrease -throughput. See `Guideline for Preventing Connection Storms: Use Static Pools -`__, -which contains more details about sizing of pools. Having a fixed size will -guarantee that the database can handle the upper pool size. For example, if a -pool needs to grow but the database resources are limited, then -:meth:`SessionPool.acquire()` may return errors such as ORA-28547. With a fixed -pool size, this class of error will occur when the pool is created, allowing you -to change the size before users access the application. With a dynamically -growing pool, the error may occur much later after the pool has been in use for -some time. - -The Real-World Performance Group also recommends keeping pool sizes small, as -they may perform better than larger pools. The pool attributes should be -adjusted to handle the desired workload within the bounds of available resources -in cx_Oracle and the database. - -Make sure the :ref:`firewall `, `resource manager -`__ -or user profile `IDLE_TIME -`__ -do not expire idle sessions, since this will require connections be recreated, -which will impact performance and scalability. - -.. _poolreconfiguration: - -Connection Pool Reconfiguration -------------------------------- - -Some pool settings can be changed dynamically with -:meth:`SessionPool.reconfigure()`. This allows the pool size and other -attributes to be changed during application runtime without needing to restart -the pool or application. - -For example a pool's size can be changed like: - -.. code-block:: python - - pool.reconfigure(min=10, max=10, increment=0) - -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, see :meth:`SessionPool.reconfigure()`. Closing -connections or closing the pool will wait until after pool reconfiguration is -complete. - -Calling ``reconfigure()`` is the only way to change a pool's ``min``, ``max`` -and ``increment`` values. Other attributes such as -:data:`~SessionPool.wait_timeout` can also be passed to ``reconfigure()`` or -they can be set directly: - -.. code-block:: python - - pool.wait_timeout = 1000 - -.. _sessioncallback: - -Session CallBacks for Setting Pooled Connection State ------------------------------------------------------ - -Applications can set "session" state in each connection. Examples of session -state are NLS settings from ``ALTER SESSION`` statements. Pooled connections -will retain their session state after they have been released back to the pool. -However, because pools can grow, or connections in the pool can be recreated, -there is no guarantee a subsequent :meth:`~SessionPool.acquire()` call will -return a database connection that has any particular state. - -The :meth:`~cx_Oracle.SessionPool()` parameter ``session_callback`` -enables efficient setting of session state so that connections have a -known session state, without requiring that state to be explicitly set -after each :meth:`~SessionPool.acquire()` call. - -Connections can also be tagged when they are released back to the pool. The -tag is a user-defined string that represents the session state of the -connection. When acquiring connections, a particular tag can be requested. If -a connection with that tag is available, it will be returned. If not, then -another session will be returned. By comparing the actual and requested tags, -applications can determine what exact state a session has, and make any -necessary changes. - -The session callback can be a Python function or a PL/SQL procedure. - -There are three common scenarios for ``session_callback``: - -- When all connections in the pool should have the same state, use a - Python callback without tagging. - -- When connections in the pool require different state for different - users, use a Python callback with tagging. - -- When using :ref:`drcp`: use a PL/SQL callback with tagging. - - -**Python Callback** - -If the ``session_callback`` parameter is a Python procedure, it will be called -whenever :meth:`~SessionPool.acquire()` will return a newly created database -connection that has not been used before. It is also called when connection -tagging is being used and the requested tag is not identical to the tag in the -connection returned by the pool. - -An example is: - -.. code-block:: python - - # Set the NLS_DATE_FORMAT for a session - def init_session(connection, requested_tag): - cursor = connection.cursor() - cursor.execute("ALTER SESSION SET NLS_DATE_FORMAT = 'YYYY-MM-DD HH24:MI'") - - # Create the pool with session callback defined - pool = cx_Oracle.SessionPool(user="hr", password=userpwd, dsn="orclpdb1", - session_callback=init_session, - encoding="UTF-8") - - # Acquire a connection from the pool (will always have the new date format) - connection = pool.acquire() - -If needed, the ``init_session()`` procedure is called internally before -``acquire()`` returns. It will not be called when previously used connections -are returned from the pool. This means that the ALTER SESSION does not need to -be executed after every ``acquire()`` call. This improves performance and -scalability. - -In this example tagging was not being used, so the ``requested_tag`` parameter -is ignored. - -Note: if you need to execute multiple SQL statements in the callback, use an -anonymous PL/SQL block to save :ref:`round-trips ` of repeated -``execute()`` calls. With ALTER SESSION, pass multiple settings in the one -statement: - -.. code-block:: python - - cursor.execute(""" - begin - execute immediate - 'alter session set nls_date_format = ''YYYY-MM-DD'' nls_language = AMERICAN'; - -- other SQL statements could be put here - end;""") - -**Connection Tagging** - -Connection tagging is used when connections in a pool should have differing -session states. In order to retrieve a connection with a desired state, the -``tag`` attribute in :meth:`~SessionPool.acquire()` needs to be set. - -When cx_Oracle is using Oracle Client libraries 12.2 or later, then cx_Oracle -uses 'multi-property tags' and the tag string must be of the form of one or -more "name=value" pairs separated by a semi-colon, for example -``"loc=uk;lang=cy"``. - -When a connection is requested with a given tag, and a connection with that tag -is not present in the pool, then a new connection, or an existing connection -with cleaned session state, will be chosen by the pool and the session callback -procedure will be invoked. The callback can then set desired session state and -update the connection's tag. However if the ``matchanytag`` parameter of -:meth:`~SessionPool.acquire()` is *True*, then any other tagged connection may -be chosen by the pool and the callback procedure should parse the actual and -requested tags to determine which bits of session state should be reset. - -The example below demonstrates connection tagging: - -.. code-block:: python - - def init_session(connection, requested_tag): - if requested_tag == "NLS_DATE_FORMAT=SIMPLE": - sql = "ALTER SESSION SET NLS_DATE_FORMAT = 'YYYY-MM-DD'" - elif requested_tag == "NLS_DATE_FORMAT=FULL": - sql = "ALTER SESSION SET NLS_DATE_FORMAT = 'YYYY-MM-DD HH24:MI'" - cursor = connection.cursor() - cursor.execute(sql) - connection.tag = requested_tag - - pool = cx_Oracle.SessionPool(user="hr", password=userpwd, dsn="orclpdb1", - session_callback=init_session, - encoding="UTF-8") - - # Two connections with different session state: - connection1 = pool.acquire(tag="NLS_DATE_FORMAT=SIMPLE") - connection2 = pool.acquire(tag="NLS_DATE_FORMAT=FULL") - -See `session_callback.py -`__ for an example. - -**PL/SQL Callback** - -When cx_Oracle uses Oracle Client 12.2 or later, the session callback can also -be the name of a PL/SQL procedure. A PL/SQL callback will be initiated only -when the tag currently associated with a connection does not match the tag that -is requested. A PL/SQL callback is most useful when using :ref:`drcp` because -DRCP does not require a :ref:`round-trip ` to invoke a PL/SQL -session callback procedure. - -The PL/SQL session callback should accept two VARCHAR2 arguments: - -.. code-block:: sql - - PROCEDURE myPlsqlCallback ( - requestedTag IN VARCHAR2, - actualTag IN VARCHAR2 - ); - -The logic in this procedure can parse the actual tag in the session that has -been selected by the pool and compare it with the tag requested by the -application. The procedure can then change any state required before the -connection is returned to the application from :meth:`~SessionPool.acquire()`. - -If the ``matchanytag`` attribute of :meth:`~SessionPool.acquire()` is *True*, -then a connection with any state may be chosen by the pool. - -Oracle 'multi-property tags' must be used. The tag string must be of the form -of one or more "name=value" pairs separated by a semi-colon, for example -``"loc=uk;lang=cy"``. - -In cx_Oracle set ``session_callback`` to the name of the PL/SQL procedure. For -example: - -.. code-block:: python - - pool = cx_Oracle.SessionPool(user="hr", password=userpwd, - dsn="dbhost.example.com/orclpdb1:pooled", - session_callback="MyPlsqlCallback", - encoding="UTF-8") - - connection = pool.acquire(tag="NLS_DATE_FORMAT=SIMPLE", - # DRCP options, if you are using DRCP - cclass='MYCLASS', - purity=cx_Oracle.ATTR_PURITY_SELF) - -See `session_callback_plsql.py -`__ for an example. - -.. _connpooltypes: - -Heterogeneous and Homogeneous Connection Pools ----------------------------------------------- - -By default, connection pools are ‘homogeneous’, meaning that all connections -use the same database credentials. However, if the pool option ``homogeneous`` -is False at the time of pool creation, then a ‘heterogeneous’ pool will be -created. This allows different credentials to be used each time a connection -is acquired from the pool with :meth:`~SessionPool.acquire()`. - -**Heterogeneous Pools** - -When a heterogeneous pool is created by setting ``homogeneous`` to False and no -credentials are supplied during pool creation, then a user name and password -may be passed to :meth:`~SessionPool.acquire()` as shown in this example: - -.. code-block:: python - - pool = cx_Oracle.SessionPool(dsn="dbhost.example.com/orclpdb1", - homogeneous=False, encoding="UTF-8") - connection = pool.acquire(user="hr", password=userpwd) - -.. _drcp: - -Database Resident Connection Pooling (DRCP) -=========================================== - -`Database Resident Connection Pooling (DRCP) -`__ enables database resource -sharing for applications that run in multiple client processes, or run on -multiple middle-tier application servers. By default each connection from -Python will use one database server process. DRCP allows pooling of these -server processes. This reduces the amount of memory required on the database -host. The DRCP pool can be shared by multiple applications. - -DRCP is useful for applications which share the same database credentials, have -similar session settings (for example date format settings or PL/SQL package -state), and where the application gets a database connection, works on it for a -relatively short duration, and then releases it. - -Applications can choose whether or not to use pooled connections at runtime. - -For efficiency, it is recommended that DRCP connections should be used -in conjunction with cx_Oracle’s local :ref:`connection pool `. - -**Using DRCP in Python** - -Using DRCP with cx_Oracle applications involves the following steps: - -1. Configuring and enabling DRCP in the database -2. Configuring the application to use a DRCP connection -3. Deploying the application - -**Configuring and enabling DRCP** - -Every instance of Oracle Database uses a single, default connection -pool. The pool can be configured and administered by a DBA using the -``DBMS_CONNECTION_POOL`` package: - -.. code-block:: sql - - EXECUTE DBMS_CONNECTION_POOL.CONFIGURE_POOL( - pool_name => 'SYS_DEFAULT_CONNECTION_POOL', - minsize => 4, - maxsize => 40, - incrsize => 2, - session_cached_cursors => 20, - inactivity_timeout => 300, - max_think_time => 600, - max_use_session => 500000, - max_lifetime_session => 86400) - -Alternatively the method ``DBMS_CONNECTION_POOL.ALTER_PARAM()`` can -set a single parameter: - -.. code-block:: sql - - EXECUTE DBMS_CONNECTION_POOL.ALTER_PARAM( - pool_name => 'SYS_DEFAULT_CONNECTION_POOL', - param_name => 'MAX_THINK_TIME', - param_value => '1200') - -The ``inactivity_timeout`` setting terminates idle pooled servers, helping -optimize database resources. To avoid pooled servers permanently being held -onto by a selfish Python script, the ``max_think_time`` parameter can be set. -The parameters ``num_cbrok`` and ``maxconn_cbrok`` can be used to distribute -the persistent connections from the clients across multiple brokers. This may -be needed in cases where the operating system per-process descriptor limit is -small. Some customers have found that having several connection brokers -improves performance. The ``max_use_session`` and ``max_lifetime_session`` -parameters help protect against any unforeseen problems affecting server -processes. The default values will be suitable for most users. See the -`Oracle DRCP documentation -`__ for details on parameters. - -In general, if pool parameters are changed, the pool should be restarted, -otherwise server processes will continue to use old settings. - -There is a ``DBMS_CONNECTION_POOL.RESTORE_DEFAULTS()`` procedure to -reset all values. - -When DRCP is used with RAC, each database instance has its own connection -broker and pool of servers. Each pool has the identical configuration. For -example, all pools start with ``minsize`` server processes. A single -DBMS_CONNECTION_POOL command will alter the pool of each instance at the same -time. The pool needs to be started before connection requests begin. The -command below does this by bringing up the broker, which registers itself with -the database listener: - -.. code-block:: sql - - EXECUTE DBMS_CONNECTION_POOL.START_POOL() - -Once enabled this way, the pool automatically restarts when the database -instance restarts, unless explicitly stopped with the -``DBMS_CONNECTION_POOL.STOP_POOL()`` command: - -.. code-block:: sql - - EXECUTE DBMS_CONNECTION_POOL.STOP_POOL() - -The pool cannot be stopped while connections are open. - -**Application Deployment for DRCP** - -In order to use DRCP, the ``cclass`` and ``purity`` parameters should -be passed to :meth:`cx_Oracle.connect()` or :meth:`SessionPool.acquire()`. If -``cclass`` is not set, the pooled server sessions will not be reused optimally, -and the DRCP statistic views will record large values for NUM_MISSES. - -The DRCP ``purity`` can be one of ``ATTR_PURITY_NEW``, ``ATTR_PURITY_SELF``, -or ``ATTR_PURITY_DEFAULT``. The value ``ATTR_PURITY_SELF`` allows reuse of -both the pooled server process and session memory, giving maximum benefit from -DRCP. See the Oracle documentation on `benefiting from scalability -`__. - -The connection string used for :meth:`~cx_Oracle.connect()` or -:meth:`~SessionPool.acquire()` must request a pooled server by -following one of the syntaxes shown below: - -Using Oracle’s Easy Connect syntax, the connection would look like: - -.. code-block:: python - - connection = cx_Oracle.connect(user="hr", password=userpwd, - dsn="dbhost.example.com/orcl:pooled", - encoding="UTF-8") - -Or if you connect using a Net Service Name named ``customerpool``: - -.. code-block:: python - - connection = cx_Oracle.connect(user="hr", password=userpwd, - dsn="customerpool", encoding="UTF-8") - -Then only the Oracle Network configuration file ``tnsnames.ora`` needs -to be modified:: - - customerpool = (DESCRIPTION=(ADDRESS=(PROTOCOL=tcp) - (HOST=dbhost.example.com) - (PORT=1521))(CONNECT_DATA=(SERVICE_NAME=CUSTOMER) - (SERVER=POOLED))) - -If these changes are made and the database is not actually configured for DRCP, -or the pool is not started, then connections will not succeed and an error will -be returned to the Python application. - -Although applications can choose whether or not to use pooled connections at -runtime, care must be taken to configure the database appropriately for the -number of expected connections, and also to stop inadvertent use of non-DRCP -connections leading to a database server resource shortage. Conversely, avoid -using DRCP connections for long-running operations. - -The example below shows how to connect to Oracle Database using Database -Resident Connection Pooling: - -.. code-block:: python - - connection = cx_Oracle.connect(user="hr", password=userpwd, - dsn="dbhost.example.com/orcl:pooled", - cclass="MYCLASS", - purity=cx_Oracle.ATTR_PURITY_SELF, - encoding="UTF-8") - -The example below shows connecting to Oracle Database using DRCP and -cx_Oracle's connection pooling: - -.. code-block:: python - - mypool = cx_Oracle.SessionPool(user="hr", password=userpwd, - dsn="dbhost.example.com/orcl:pooled", - encoding="UTF-8") - connection = mypool.acquire(cclass="MYCLASS", - purity=cx_Oracle.ATTR_PURITY_SELF) - -For more information about DRCP see `Oracle Database Concepts Guide -`__, and for DRCP Configuration -see `Oracle Database Administrator's Guide -`__. - -**Closing Connections** - -Python scripts where cx_Oracle connections do not go out of scope quickly -(which releases them), or do not currently use :meth:`Connection.close()`, -should be examined to see if :meth:`~Connection.close()` can be used, which -then allows maximum use of DRCP pooled servers by the database: - -.. code-block:: python - - # Do some database operations - connection = mypool.acquire(cclass="MYCLASS", purity=cx_Oracle.ATTR_PURITY_SELF) - . . . - connection.close(); - - # Do lots of non-database work - . . . - - # Do some more database operations - connection = mypool.acquire(cclass="MYCLASS", purity=cx_Oracle.ATTR_PURITY_SELF) - . . . - connection.close(); - -**Monitoring DRCP** - -Data dictionary views are available to monitor the performance of DRCP. -Database administrators can check statistics such as the number of busy and -free servers, and the number of hits and misses in the pool against the total -number of requests from clients. The views are: - -* ``DBA_CPOOL_INFO`` -* ``V$PROCESS`` -* ``V$SESSION`` -* ``V$CPOOL_STATS`` -* ``V$CPOOL_CC_STATS`` -* ``V$CPOOL_CONN_INFO`` - -**DBA_CPOOL_INFO View** - -``DBA_CPOOL_INFO`` displays configuration information about the DRCP pool. The -columns are equivalent to the ``dbms_connection_pool.configure_pool()`` -settings described in the table of DRCP configuration options, with the -addition of a ``STATUS`` column. The status is ``ACTIVE`` if the pool has been -started and ``INACTIVE`` otherwise. Note the pool name column is called -``CONNECTION_POOL``. This example checks whether the pool has been started and -finds the maximum number of pooled servers:: - - SQL> SELECT connection_pool, status, maxsize FROM dba_cpool_info; - - CONNECTION_POOL STATUS MAXSIZE - ---------------------------- ---------- ---------- - SYS_DEFAULT_CONNECTION_POOL ACTIVE 40 - -**V$PROCESS and V$SESSION Views** - -The ``V$SESSION`` view shows information about the currently active DRCP -sessions. It can also be joined with ``V$PROCESS`` via -``V$SESSION.PADDR = V$PROCESS.ADDR`` to correlate the views. - -**V$CPOOL_STATS View** - -The ``V$CPOOL_STATS`` view displays information about the DRCP statistics for -an instance. The V$CPOOL_STATS view can be used to assess how efficient the -pool settings are. T his example query shows an application using the pool -effectively. The low number of misses indicates that servers and sessions were -reused. The wait count shows just over 1% of requests had to wait for a pooled -server to become available:: - - NUM_REQUESTS NUM_HITS NUM_MISSES NUM_WAITS - ------------ ---------- ---------- ---------- - 10031 99990 40 1055 - -If ``cclass`` was set (allowing pooled servers and sessions to be -reused) then NUM_MISSES will be low. If the pool maxsize is too small for -the connection load, then NUM_WAITS will be high. - -**V$CPOOL_CC_STATS View** - -The view ``V$CPOOL_CC_STATS`` displays information about the connection class -level statistics for the pool per instance:: - - SQL> SELECT cclass_name, num_requests, num_hits, num_misses - FROM v$cpool_cc_stats; - - CCLASS_NAME NUM_REQUESTS NUM_HITS NUM_MISSES - -------------------------------- ------------ ---------- ---------- - HR.MYCLASS 100031 99993 38 - -**V$CPOOL_CONN_INFO View** - -The ``V$POOL_CONN_INFO`` view gives insight into client processes that are -connected to the connection broker, making it easier to monitor and trace -applications that are currently using pooled servers or are idle. This view was -introduced in Oracle 11gR2. - -You can monitor the view ``V$CPOOL_CONN_INFO`` to, for example, identify -misconfigured machines that do not have the connection class set correctly. -This view maps the machine name to the class name:: - - SQL> SELECT cclass_name, machine FROM v$cpool_conn_info; - - CCLASS_NAME MACHINE - --------------------------------------- ------------ - CJ.OCI:SP:wshbIFDtb7rgQwMyuYvodA cjlinux - . . . - -In this example you would examine applications on ``cjlinux`` and make -sure ``cclass`` is set. - - -.. _proxyauth: - -Connecting Using Proxy Authentication -===================================== - -Proxy authentication allows a user (the "session user") to connect to Oracle -Database using the credentials of a 'proxy user'. Statements will run as the -session user. Proxy authentication is generally used in three-tier applications -where one user owns the schema while multiple end-users access the data. For -more information about proxy authentication, see the `Oracle documentation -`__. - -An alternative to using proxy users is to set -:attr:`Connection.client_identifier` after connecting and use its value in -statements and in the database, for example for :ref:`monitoring -`. - -The following proxy examples use these schemas. The ``mysessionuser`` schema is -granted access to use the password of ``myproxyuser``: - -.. code-block:: sql - - CREATE USER myproxyuser IDENTIFIED BY myproxyuserpw; - GRANT CREATE SESSION TO myproxyuser; - - CREATE USER mysessionuser IDENTIFIED BY itdoesntmatter; - GRANT CREATE SESSION TO mysessionuser; - - ALTER USER mysessionuser GRANT CONNECT THROUGH myproxyuser; - -After connecting to the database, the following query can be used to show the -session and proxy users: - -.. code-block:: sql - - SELECT SYS_CONTEXT('USERENV', 'PROXY_USER'), - SYS_CONTEXT('USERENV', 'SESSION_USER') - FROM DUAL; - -Standalone connection examples: - -.. code-block:: python - - # Basic Authentication without a proxy - connection = cx_Oracle.connect(user="myproxyuser", - password="myproxyuserpw", - dsn="dbhost.example.com/orclpdb1", - encoding="UTF-8") - # PROXY_USER: None - # SESSION_USER: MYPROXYUSER - - # Basic Authentication with a proxy - connection = cx_Oracle.connect(user="myproxyuser[mysessionuser]", - password="myproxyuserpw", - dsn="dbhost.example.com/orclpdb1", - encoding="UTF-8") - # PROXY_USER: MYPROXYUSER - # SESSION_USER: MYSESSIONUSER - -Pooled connection examples: - -.. code-block:: python - - # Basic Authentication without a proxy - pool = cx_Oracle.SessionPool(user="myproxyuser", password="myproxyuser", - dsn="dbhost.example.com/orclpdb1", - encoding="UTF-8") - connection = pool.acquire() - # PROXY_USER: None - # SESSION_USER: MYPROXYUSER - - # Basic Authentication with proxy - pool = cx_Oracle.SessionPool(user="myproxyuser[mysessionuser]", - password="myproxyuser", - dsn="dbhost.example.com/orclpdb1", - homogeneous=False, encoding="UTF-8") - connection = pool.acquire() - # PROXY_USER: MYPROXYUSER - # SESSION_USER: MYSESSIONUSER - -Note the use of a :ref:`heterogeneous ` pool in the example -above. This is required in this scenario. - -.. _extauth: - -Connecting Using External Authentication -======================================== - -Instead of storing the database username and password in Python scripts or -environment variables, database access can be authenticated by an outside -system. External Authentication allows applications to validate user access by -an external password store (such as an Oracle Wallet), by the operating system, -or with an external authentication service. - -Using an Oracle Wallet for External Authentication --------------------------------------------------- - -The following steps give an overview of using an Oracle Wallet. Wallets should -be kept securely. Wallets can be managed with `Oracle Wallet Manager -`__. - -In this example the wallet is created for the ``myuser`` schema in the directory -``/home/oracle/wallet_dir``. The ``mkstore`` command is available from a full -Oracle client or Oracle Database installation. If you have been given wallet by -your DBA, skip to step 3. - -1. First create a new wallet as the ``oracle`` user:: - - mkstore -wrl "/home/oracle/wallet_dir" -create - - This will prompt for a new password for the wallet. - -2. Create the entry for the database user name and password that are currently - hardcoded in your Python scripts. Use either of the methods shown below. - They will prompt for the wallet password that was set in the first step. - - **Method 1 - Using an Easy Connect string**:: - - mkstore -wrl "/home/oracle/wallet_dir" -createCredential dbhost.example.com/orclpdb1 myuser myuserpw - - **Method 2 - Using a connect name identifier**:: - - mkstore -wrl "/home/oracle/wallet_dir" -createCredential mynetalias myuser myuserpw - - The alias key ``mynetalias`` immediately following the - ``-createCredential`` option will be the connect name to be used in Python - scripts. If your application connects with multiple different database - users, you could create a wallet entry with different connect names for - each. - - You can see the newly created credential with:: - - mkstore -wrl "/home/oracle/wallet_dir" -listCredential - -3. Skip this step if the wallet was created using an Easy Connect String. - Otherwise, add an entry in :ref:`tnsnames.ora ` for the connect - name as follows:: - - mynetalias = - (DESCRIPTION = - (ADDRESS = (PROTOCOL = TCP)(HOST = dbhost.example.com)(PORT = 1521)) - (CONNECT_DATA = - (SERVER = DEDICATED) - (SERVICE_NAME = orclpdb1) - ) - ) - - The file uses the description for your existing database and sets the - connect name alias to ``mynetalias``, which is the identifier used when - adding the wallet entry. - -4. Add the following wallet location entry in the :ref:`sqlnet.ora - ` file, using the ``DIRECTORY`` you created the wallet in:: - - WALLET_LOCATION = - (SOURCE = - (METHOD = FILE) - (METHOD_DATA = - (DIRECTORY = /home/oracle/wallet_dir) - ) - ) - SQLNET.WALLET_OVERRIDE = TRUE - - Examine the Oracle documentation for full settings and values. - -5. Ensure the configuration files are in a default location or set TNS_ADMIN is - set to the directory containing them. See :ref:`optnetfiles`. - -With an Oracle wallet configured, and readable by you, your scripts -can connect using: - -.. code-block:: python - - connection = cx_Oracle.connect(dsn="mynetalias", encoding="UTF-8") - -or: - -.. code-block:: python - - pool = cx_Oracle.SessionPool(externalauth=True, homogeneous=False, - dsn="mynetalias", encoding="UTF-8") - pool.acquire() - -The ``dsn`` must match the one used in the wallet. - -After connecting, the query:: - - SELECT SYS_CONTEXT('USERENV', 'SESSION_USER') FROM DUAL; - -will show:: - - MYUSER - -.. note:: - - Wallets are also used to configure TLS connections. If you are using a - wallet like this, you may need a database username and password in - :meth:`cx_Oracle.connect()` and :meth:`cx_Oracle.SessionPool()` calls. - -**External Authentication and Proxy Authentication** - -The following examples show external wallet authentication combined with -:ref:`proxy authentication `. These examples use the wallet -configuration from above, with the addition of a grant to another user:: - - ALTER USER mysessionuser GRANT CONNECT THROUGH myuser; - -After connection, you can check who the session user is with: - -.. code-block:: sql - - SELECT SYS_CONTEXT('USERENV', 'PROXY_USER'), - SYS_CONTEXT('USERENV', 'SESSION_USER') - FROM DUAL; - -Standalone connection example: - -.. code-block:: python - - # External Authentication with proxy - connection = cx_Oracle.connect(user="[mysessionuser]", dsn="mynetalias", - encoding="UTF-8") - # PROXY_USER: MYUSER - # SESSION_USER: MYSESSIONUSER - -Pooled connection example: - -.. code-block:: python - - # External Authentication with proxy - pool = cx_Oracle.SessionPool(externalauth=True, homogeneous=False, - dsn="mynetalias", encoding="UTF-8") - pool.acquire(user="[mysessionuser]") - # PROXY_USER: MYUSER - # SESSION_USER: MYSESSIONUSER - -The following usage is not supported: - -.. code-block:: python - - pool = cx_Oracle.SessionPool(user="[mysessionuser]", externalauth=True, - homogeneous=False, dsn="mynetalias", - encoding="UTF-8") - pool.acquire() - - -Operating System Authentication -------------------------------- - -With Operating System authentication, Oracle allows user authentication to be -performed by the operating system. The following steps give an overview of how -to implement OS Authentication on Linux. - -1. Login to your computer. The commands used in these steps assume the - operating system user name is "oracle". - -2. Login to SQL*Plus as the SYSTEM user and verify the value for the - ``OS_AUTHENT_PREFIX`` parameter:: - - SQL> SHOW PARAMETER os_authent_prefix - - NAME TYPE VALUE - ------------------------------------ ----------- ------------------------------ - os_authent_prefix string ops$ - -3. Create an Oracle database user using the ``os_authent_prefix`` determined in - step 2, and the operating system user name: - - .. code-block:: sql - - CREATE USER ops$oracle IDENTIFIED EXTERNALLY; - GRANT CONNECT, RESOURCE TO ops$oracle; - -In Python, connect using the following code: - -.. code-block:: python - - connection = cx_Oracle.connect(dsn="mynetalias", encoding="UTF-8") - -Your session user will be ``OPS$ORACLE``. - -If your database is not on the same computer as python, you can perform testing -by setting the database configuration parameter ``remote_os_authent=true``. -Beware this is insecure. - -See `Oracle Database Security Guide -`__ for more information about -Operating System Authentication. - -Privileged Connections -====================== - -The ``mode`` parameter of the function :meth:`cx_Oracle.connect()` specifies -the database privilege that you want to associate with the user. - -The example below shows how to connect to Oracle Database as SYSDBA: - -.. code-block:: python - - connection = cx_Oracle.connect(user="sys", password=syspwd, - dsn="dbhost.example.com/orclpdb1", - mode=cx_Oracle.SYSDBA, encoding="UTF-8") - - cursor = con.cursor() - sql = "GRANT SYSOPER TO hr" - cursor.execute(sql) - -This is equivalent to executing the following in SQL*Plus: - -.. code-block:: sql - - CONNECT sys/syspwd AS SYSDBA - - GRANT SYSOPER TO hr; - -.. _netencrypt: - -Securely Encrypting Network Traffic to Oracle Database -====================================================== - -You can encrypt data transferred between the Oracle Database and the Oracle -Client libraries used by cx_Oracle so that unauthorized parties are not able to -view plain text values as the data passes over the network. The easiest -configuration is Oracle’s native network encryption. The standard SSL protocol -can also be used if you have a PKI, but setup is necessarily more involved. - -With native network encryption, the client and database server negotiate a key -using Diffie-Hellman key exchange. This provides protection against -man-in-the-middle attacks. - -Native network encryption can be configured by editing Oracle Net’s optional -:ref:`sqlnet.ora ` configuration file, on either the database -server and/or on each cx_Oracle 'client' machine. Parameters control whether -data integrity checking and encryption is required or just allowed, and which -algorithms the client and server should consider for use. - -As an example, to ensure all connections to the database are checked for -integrity and are also encrypted, create or edit the Oracle Database -``$ORACLE_HOME/network/admin/sqlnet.ora`` file. Set the checksum negotiation -to always validate a checksum and set the checksum type to your desired value. -The network encryption settings can similarly be set. For example, to use the -SHA512 checksum and AES256 encryption use:: - - SQLNET.CRYPTO_CHECKSUM_SERVER = required - SQLNET.CRYPTO_CHECKSUM_TYPES_SERVER = (SHA512) - SQLNET.ENCRYPTION_SERVER = required - SQLNET.ENCRYPTION_TYPES_SERVER = (AES256) - -If you definitely know that the database server enforces integrity and -encryption, then you do not need to configure cx_Oracle separately. However -you can also, or alternatively, do so depending on your business needs. Create -a ``sqlnet.ora`` on your client machine and locate it with other -:ref:`optnetfiles`:: - - SQLNET.CRYPTO_CHECKSUM_CLIENT = required - SQLNET.CRYPTO_CHECKSUM_TYPES_CLIENT = (SHA512) - SQLNET.ENCRYPTION_CLIENT = required - SQLNET.ENCRYPTION_TYPES_CLIENT = (AES256) - -The client and server sides can negotiate the protocols used if the settings -indicate more than one value is accepted. - -Note that these are example settings only. You must review your security -requirements and read the documentation for your Oracle version. In particular -review the available algorithms for security and performance. - -The ``NETWORK_SERVICE_BANNER`` column of the database view -`V$SESSION_CONNECT_INFO -`__ can be used to verify the -encryption status of a connection. - -For more information on Oracle Data Network Encryption and Integrity, -configuring SSL network encryption and Transparent Data Encryption of -data-at-rest in the database, see `Oracle Database Security Guide -`__. - - -Resetting Passwords -=================== - -After connecting, passwords can be changed by calling -:meth:`Connection.changepassword()`: - -.. code-block:: python - - # Get the passwords from somewhere, such as prompting the user - oldpwd = getpass.getpass(f"Old Password for {username}: ") - newpwd = getpass.getpass(f"New Password for {username}: ") - - connection.changepassword(oldpwd, newpwd) - -When a password has expired and you cannot connect directly, you can connect -and change the password in one operation by using the ``newpassword`` parameter -of the function :meth:`cx_Oracle.connect()` constructor: - -.. code-block:: python - - # Get the passwords from somewhere, such as prompting the user - oldpwd = getpass.getpass(f"Old Password for {username}: ") - newpwd = getpass.getpass(f"New Password for {username}: ") - - connection = cx_Oracle.connect(user=username, password=oldpwd, - dsn="dbhost.example.com/orclpdb1", - newpassword=newpwd, encoding="UTF-8") - -.. _autononmousdb: - -Connecting to Oracle Cloud Autononmous Databases -================================================ - -To enable connection to Oracle Autonomous Database in Oracle Cloud, a wallet -needs be downloaded from the cloud, and cx_Oracle needs to be configured to use -it. The wallet gives mutual TLS which provides enhanced security for -authentication and encryption. A database username and password is still -required for your application connections. - -Install the Wallet and Network Configuration Files --------------------------------------------------- - -From the Oracle Cloud console for the database, download the wallet zip file. -It contains the wallet and network configuration files. Note: keep wallet -files in a secure location and share them only with authorized users. - -Unzip the wallet zip file. For cx_Oracle, only these files from the zip are needed: - -- ``tnsnames.ora`` - Maps net service names used for application connection strings to your database services -- ``sqlnet.ora`` - Configures Oracle Network settings -- ``cwallet.sso`` - Enables SSL/TLS connections - -There are now two options: - -- Move the three files to the ``network/admin`` directory of the client - libraries used by your application. For example if you are using Instant - Client 19c and it is in ``$HOME/instantclient_19_11``, then you would put the - wallet files in ``$HOME/instantclient_19_11/network/admin/``. - -- Alternatively, move them to any accessible directory, for example - ``/opt/OracleCloud/MYDB``. - - Then edit ``sqlnet.ora`` and change the wallet location directory to the - directory containing the ``cwallet.sso`` file. For example:: - - WALLET_LOCATION = (SOURCE = (METHOD = file) (METHOD_DATA = (DIRECTORY="/opt/OracleCloud/MYDB"))) - SSL_SERVER_DN_MATCH=yes - - Since the ``tnsnames.ora`` and ``sqlnet.ora`` files are not in the default - location, your application needs to indicate where they are, either with the - ``config_dir`` parameter to :meth:`cx_Oracle.init_oracle_client()`, or using - the ``TNS_ADMIN`` environment variable. See :ref:`Optional Oracle Net - Configuration Files `. Neither of these settings are needed, - and you don't need to edit ``sqlnet.ora``, if you have put all the files in - the ``network/admin`` directory. - -Run Your Application --------------------- - -The ``tnsnames.ora`` file contains net service names for various levels of -database service. For example, if you create a database called CJDB1 with the -Always Free services from the `Oracle Cloud Free Tier -`__, then you might decide to use the -connection string in ``tnsnames.ora`` called ``cjdb1_high``. - -Update your application to use your schema username, its database password, and -a net service name, for example: - -.. code-block:: python - - connection = cx_Oracle.connect(user="scott", password=userpwd, - dsn="cjdb1_high", encoding="UTF-8") - -Once you have set optional Oracle environment variables used by your -application, such as ``TNS_ADMIN``, you can start your application. - -If you need to create a new database schema so you do not login as the -privileged ADMIN user, refer to the relevant Oracle Cloud documentation, for -example see `Create Database Users -`__ -in the Oracle Autonomous Database manual. - -Access Through a Proxy ----------------------- - -If you are behind a firewall, you can tunnel TLS/SSL connections via a proxy -using `HTTPS_PROXY -`__ -in the connect descriptor. Successful connection depends on specific proxy -configurations. Oracle does not recommend doing this when performance is -critical. - -Edit ``sqlnet.ora`` and add a line:: - - SQLNET.USE_HTTPS_PROXY=on - -Edit ``tnsnames.ora`` and add an ``HTTPS_PROXY`` proxy name and -``HTTPS_PROXY_PORT`` port to the connect descriptor address list of any service -name you plan to use, for example:: - - - cjdb1_high = (description= - (address= - (https_proxy=myproxy.example.com)(https_proxy_port=80) - (protocol=tcps)(port=1522)(host= . . . ) - -Using the Easy Connect Syntax with Autonomous Database ------------------------------------------------------- - -When cx_Oracle is using Oracle Client libraries 19c or later, you can -optionally use the :ref:`Easy Connect ` syntax to connect to -Oracle Autonomous Database. - -The mapping from the cloud ``tnsnames.ora`` entries to an Easy Connect Plus -string is:: - - protocol://host:port/service_name?wallet_location=/my/dir&retry_count=N&retry_delay=N - -For example, if your ``tnsnames.ora`` file had an entry:: - - cjjson_high = (description=(retry_count=20)(retry_delay=3) - (address=(protocol=tcps)(port=1522) - (host=adb.ap-sydney-1.oraclecloud.com)) - (connect_data=(service_name=abc_cjjson_high.adb.oraclecloud.com)) - (security=(ssl_server_cert_dn="CN=adb.ap-sydney-1.oraclecloud.com,OU=Oracle ADB SYDNEY,O=Oracle Corporation,L=Redwood City,ST=California,C=US"))) - -Then your applications can connect using the connection string: - - .. code-block:: python - - dsn = "tcps://adb.ap-sydney-1.oraclecloud.com:1522/abc_cjjson_high.adb.oraclecloud.com?wallet_location=/Users/cjones/Cloud/CJJSON&retry_count=20&retry_delay=3" - connection = cx_Oracle.connect(user="hr", password=userpwd, dsn=dsn, - encoding="UTF-8") - -The ``wallet_location`` parameter needs to be set to the directory containing -the ``cwallet.sso`` file from the wallet ZIP. The other wallet files, -including ``tnsnames.ora``, are not needed when you use the Easy Connect Plus -syntax. - -You can add other Easy Connect parameters to the connection string, for example:: - - dsn = dsn + "&https_proxy=myproxy.example.com&https_proxy_port=80" - -.. _connsharding: - -Connecting to Sharded Databases -=============================== - -`Oracle Sharding -`__ -can be used to horizontally partition data across independent databases. A -database table can be split so each shard contains a table with the same columns -but a different subset of rows. These tables are known as sharded tables. -Sharding is configured in Oracle Database, see the `Oracle Sharding -`__ manual. -Sharding requires Oracle Database and Oracle Client libraries 12.2, or later. - -The :meth:`cx_Oracle.connect()` and :meth:`SessionPool.acquire()` functions -accept ``shardingkey`` and ``supershardingkey`` parameters that are a sequence -of values used to route the connection directly to a given shard. A sharding -key is always required. A super sharding key is additionally required when -using composite sharding, which is when data has been partitioned by a list or -range (the super sharding key), and then further partitioned by a sharding key. - -When creating a connection pool, the :meth:`cx_Oracle.SessionPool()` attribute -``max_sessions_per_shard`` can be set. This is used to balance connections in -the pool equally across shards. It requires Oracle Client libraries 18.3, or -later. - -Shard key values may be of type string (mapping to VARCHAR2 shard keys), number -(NUMBER), bytes (RAW), or date (DATE). Multiple types may be used in each -array. Sharding keys of TIMESTAMP type are not supported. - -When connected to a shard, queries will only return data from that shard. For -queries that need to access data from multiple shards, connections can be -established to the coordinator shard catalog database. In this case, no shard -key or super shard key is used. - -As an example of direct connection, if sharding had been configured on a single -VARCHAR2 column like: - -.. code-block:: sql - - CREATE SHARDED TABLE customers ( - cust_id NUMBER, - cust_name VARCHAR2(30), - class VARCHAR2(10) NOT NULL, - signup_date DATE, - cust_code RAW(20), - CONSTRAINT cust_name_pk PRIMARY KEY(cust_name)) - PARTITION BY CONSISTENT HASH (cust_name) - PARTITIONS AUTO TABLESPACE SET ts1; - -then direct connection to a shard can be made by passing a single sharding key: - -.. code-block:: python - - connection = cx_Oracle.connect(user="hr", password=userpwd, - dsn="dbhost.example.com/orclpdb1", - encoding="UTF-8", shardingkey=["SCOTT"]) - -Numbers keys can be used in a similar way: - -.. code-block:: python - - connection = cx_Oracle.connect(user="hr", password=userpwd, - dsn="dbhost.example.com/orclpdb1", - encoding="UTF-8", shardingkey=[110]) - -When sharding by DATE, you can connect like: - -.. code-block:: python - - import datetime - - d = datetime.datetime(2014, 7, 3) - - connection = cx_Oracle.connect(user="hr", password=userpwd, - dsn="dbhost.example.com/orclpdb1", - encoding="UTF-8", shardingkey=[d]) - -When sharding by RAW, you can connect like: - -.. code-block:: python - - b = b'\x01\x04\x08'; - - connection = cx_Oracle.connect(user="hr", password=userpwd, - dsn="dbhost.example.com/orclpdb1", - encoding="UTF-8", shardingkey=[b]) - -Multiple keys can be specified, for example: - -.. code-block:: python - - key_list = [70, "SCOTT", "gold", b'\x00\x01\x02'] - - connection = cx_Oracle.connect(user="hr", password=userpwd, - dsn="dbhost.example.com/orclpdb1", - encoding="UTF-8", shardingkey=key_list) - -A super sharding key example is: - -.. code-block:: python - - connection = cx_Oracle.connect(user="hr", password=userpwd, - dsn="dbhost.example.com/orclpdb1", - encoding="UTF-8", - supershardingkey=["goldclass"], - shardingkey=["SCOTT"]) +See `Connecting to Oracle Database `__ in the python-oracledb +documentation. diff --git a/doc/src/user_guide/cqn.rst b/doc/src/user_guide/cqn.rst index d4d2293e..e5d7ff00 100644 --- a/doc/src/user_guide/cqn.rst +++ b/doc/src/user_guide/cqn.rst @@ -4,154 +4,8 @@ Continuous Query Notification (CQN) *********************************** -`Continuous Query Notification (CQN) -`__ 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. +.. include:: ../note.rst -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 (previously known as Database Change Notification) -occurs and the Python notification method is invoked whenever a database -transaction is committed that changes an object that a registered query -references, regardless of whether the actual query result changed. However if -the :meth:`subscription ` option ``qos`` is -:data:`cx_Oracle.SUBSCR_QOS_QUERY` then query-level notification occurs. In -this mode, the database notifies the application whenever a transaction changing -the result of the registered query is committed. - -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 ; - -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 'CUSTOMER' 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 -`__ -for a runnable CQN example. +See `Working with Continuous Query Notification (CQN) `__ in the python-oracledb +documentation. diff --git a/doc/src/user_guide/exception_handling.rst b/doc/src/user_guide/exception_handling.rst index e27075b1..ce9f29f5 100644 --- a/doc/src/user_guide/exception_handling.rst +++ b/doc/src/user_guide/exception_handling.rst @@ -4,37 +4,7 @@ Exception Handling ****************** -All exceptions raised by cx_Oracle are inherited from :attr:`cx_Oracle.Error`. -See :ref:`Exceptions ` for more details on the various exceptions -defined by cx_Oracle. See the exception handling section in the -:ref:`API manual ` for more details on the information available -when an exception is raised. +.. include:: ../note.rst -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") +See `Catching Exceptions `__ in the python-oracledb documentation. diff --git a/doc/src/user_guide/globalization.rst b/doc/src/user_guide/globalization.rst index 2d6f947f..eb8a4300 100644 --- a/doc/src/user_guide/globalization.rst +++ b/doc/src/user_guide/globalization.rst @@ -4,154 +4,8 @@ Character Sets and Globalization ******************************** -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" `. +.. include:: ../note.rst -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 -`__. - - -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 -`__ 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 -`__ - -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 -`__. +See `Character Sets and Globalization `__ in the python-oracledb +documentation. diff --git a/doc/src/user_guide/ha.rst b/doc/src/user_guide/ha.rst index 70bc3e53..a9de207b 100644 --- a/doc/src/user_guide/ha.rst +++ b/doc/src/user_guide/ha.rst @@ -4,213 +4,9 @@ High Availability with cx_Oracle ******************************** -Applications can utilize many features for high availability (HA) during planned and -unplanned outages in order to: +.. include:: ../note.rst -* Reduce application downtime -* Eliminate compromises between high availability and performance -* Increase operational productivity +See `Using High Availability with python-oracledb `__ in the python-oracledb +documentation. -.. _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 `__ or standby databases. -* Configure database services to emit :ref:`FAN ` events. -* Use a :ref:`connection pool `, 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 ` -should be configured for performance and availability. - -Options such as `SQLNET.OUTBOUND_CONNECT_TIMEOUT -`__, -`SQLNET.RECV_TIMEOUT -`__ -and `SQLNET.SEND_TIMEOUT -`__ -can be explored. - -`Oracle Net Services -`__ options may -also be useful for high availability and performance tuning. For example the -database's `listener.ora` file can have `RATE_LIMIT -`__ -and `QUEUESIZE -`__ -parameters that can help handle connection storms. - -With Oracle Client 19c, `EXPIRE_TIME -`__ -can be used in :ref:`tnsnames.ora ` 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 -`_ -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 `, then the -:ref:`Easy Connect Plus syntax ` 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 -`__ -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 `. - -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 ` 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 -`__ -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 -`__. - -.. _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 -`__ -for more information. - -.. _tg: - -Transaction Guard ------------------ - -cx_Oracle supports `Transaction Guard -`__ 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 -`__ 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 ; - -2. Create a new service by executing the following PL/SQL block as SYSDBA. - Replace the ````, ```` and - ```` 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') := ; - DBMS_SERVICE.CREATE_SERVICE('', '', t_Params); - END; - / - -3. Start the service by executing the following PL/SQL block as SYSDBA: - - .. code-block:: sql - - BEGIN - DBMS_SERVICE.start_service(''); - 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 -`__ for further details. diff --git a/doc/src/user_guide/initialization.rst b/doc/src/user_guide/initialization.rst index 5abd9295..b4b721cd 100644 --- a/doc/src/user_guide/initialization.rst +++ b/doc/src/user_guide/initialization.rst @@ -4,405 +4,8 @@ cx_Oracle 8 Initialization ************************** -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. +.. include:: ../note.rst -.. 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 -`__. - - -.. _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 `. - -* ``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 - `__ 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 -`__ -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:`autononmousdb`. - -Note the :ref:`easyconnect` can set many common configuration options without -needing ``tnsnames.ora`` or ``sqlnet.ora`` files. - -The section :ref:`Network Configuration ` 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:: - - - - - - 1000 - - - - -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 ` 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 ` events which affects notifications and Runtime Load Balancing (RLB). -- Configuring `Client Result Caching `__ parameters -- Turning on `Client Statement Cache Auto-tuning `__ - -Refer to the documentation on `oraaccess.xml -`__ -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 -`__. - -.. _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. +See `Initializing python-oracledb `__ in the python-oracledb +documentation. diff --git a/doc/src/user_guide/installation.rst b/doc/src/user_guide/installation.rst index 62d9f84c..d7094b0f 100644 --- a/doc/src/user_guide/installation.rst +++ b/doc/src/user_guide/installation.rst @@ -4,910 +4,7 @@ cx_Oracle 8 Installation ************************ -Overview -======== +.. include:: ../note.rst -To use cx_Oracle 8 with Python and Oracle Database you need: - -- Python 3.5 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 - `__, 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 -================================== - -The `Quick Start: Developing Python Applications for Oracle Database -`__ -and `Quick Start: Developing Python Applications for Oracle Autonomous Database -`__ -instructions have steps for Windows, Linux, and macOS. - -Alternatively you can: - -- Install `Python `__ 3, if not already - available. On macOS you must always install your own Python. - - Python 3.5 and higher are supported by cx_Oracle 8. If you use Python 2, - then the older cx_Oracle 7.3 will install. - -- Install cx_Oracle from `PyPI - `__ 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 - `__ - "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") - `__ - 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 ` -and `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 -`__ -"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 -`__. -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 -`__ external configuration -file available with 12.1 or later clients, session pool improvements, -improved high availability features, call timeouts, and `other enhancements -`__. - -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`. - -Install cx_Oracle ------------------ - -The generic way to install cx_Oracle on Linux is to use Python's `Pip -`__ package to -install cx_Oracle from `PyPI -`__: - -.. 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 `__ 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 - `__ - "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") - `__ - 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 `__ - - `x86 32-bit `__ - - `ARM (aarch64) 64-bit `__ - - 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 `__ - - `x86 32-bit `__ - - `ARM (aarch64) 64-bit `__ - - Oracle's yum server has convenient repositories: - - - `Instant Client 21 RPMs for Oracle Linux x86-64 8 `__, `Older Instant Client RPMs for Oracle Linux x86-64 8 `__ - - `Instant Client 21 RPMs for Oracle Linux x86-64 7 `__, `Older Instant Client RPMs for Oracle Linux x86-64 7 `__ - - `Instant Client RPMs for Oracle Linux x86-64 6 `__ - - `Instant Client RPMs for Oracle Linux ARM (aarch64) 8 `__ - - `Instant Client RPMs for Oracle Linux ARM (aarch64) 7 `__ - - 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 -`__. 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 `__. - -.. _wininstall: - -Installing cx_Oracle on Windows -=============================== - -Install cx_Oracle ------------------ - -Use Python's `Pip `__ -package to install cx_Oracle from `PyPI -`__:: - - 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 `__ 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 - `__ - "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") - `__ - 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 - `__ - or `32-bit - `__, 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 19 install `VS 2017 `__. - - For Instant Client 18 or 12.2 install `VS 2013 `__ - - For Instant Client 12.1 install `VS 2010 `__ - - For Instant Client 11.2 install `VS 2005 64-bit `__ or `VS 2005 32-bit `__ - -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) -========================================= - -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 `__ or `Python.org -`__. - -A C compiler is needed, for example Xcode and its command line tools. - -Install cx_Oracle ------------------ - -Use Python's `Pip `__ -package to install cx_Oracle from `PyPI -`__: - -.. 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 - `__. - -* 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 -`__. - -Pre-built images for Python and cx_Oracle are in the `GitHub Container Registry -`__. 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 -`__. Transfer this file to -the offline computer and install it with:: - - python -m pip install "" - -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 -`__ 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 `__ 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 ` and :ref:`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 -`__ 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 `__. - - -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 - `__. - - - 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 - `__ 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 - `__ or `Python.org - `__ 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. +See `Installing python-oracledb `__ in the python-oracledb documentation. diff --git a/doc/src/user_guide/introduction.rst b/doc/src/user_guide/introduction.rst index 9e9ddb08..75a84777 100644 --- a/doc/src/user_guide/introduction.rst +++ b/doc/src/user_guide/introduction.rst @@ -4,141 +4,8 @@ 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 -`__ with a considerable number of -additions and a couple of exclusions. +.. include:: ../note.rst -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 -`__ using `pip -`__. The Oracle Client libraries -need to be installed separately. The libraries can be from an installation of -`Oracle Instant Client -`__, 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 `. - -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 `. 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 -`_. - -Getting Started ---------------- - -Install cx_Oracle using the :ref:`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 -`__. - -Simple :ref:`connection ` to the database requires a username, -password and connection string. Locate your Oracle Database `user name and -password `_ and the database -:ref:`connection string `, 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 ` 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 ` 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 ----------------------- - -The `Quick Start: Developing Python Applications for Oracle Database -`__ -and `Quick Start: Developing Python Applications for Oracle Autonomous Database -`__ -instructions have steps for Windows, Linux, and macOS. - -Runnable examples are in the `GitHub samples directory -`__. A `Python -cx_Oracle tutorial -`__ -is also available. +See `Introduction to the Python Driver for Oracle Database +`__ in the python-oracledb documentation. diff --git a/doc/src/user_guide/json_data_type.rst b/doc/src/user_guide/json_data_type.rst index bbd0dfb9..53047f7a 100644 --- a/doc/src/user_guide/json_data_type.rst +++ b/doc/src/user_guide/json_data_type.rst @@ -4,307 +4,7 @@ Working with the JSON Data Type ******************************* -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) `, which allows -access to JSON documents through a set of NoSQL-style APIs. +.. include:: ../note.rst -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 -`__ 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 -`__. - -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 -`__ -in the Oracle JSON Developer's Guide. - - -Accessing Relational Data as JSON -================================= - -In Oracle Database 12.2, or later, the `JSON_OBJECT -`__ -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"}',) +See `Using JSON Data `__ in the python-oracledb documentation. diff --git a/doc/src/user_guide/lob_data.rst b/doc/src/user_guide/lob_data.rst index 39ae12ba..1427a757 100644 --- a/doc/src/user_guide/lob_data.rst +++ b/doc/src/user_guide/lob_data.rst @@ -4,197 +4,8 @@ Using CLOB and BLOB Data ************************ -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. +.. include:: ../note.rst -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 `__ 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`. 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 ` 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. +See `Using CLOB, BLOB, NCLOB, and BFILE Data `__ in the +python-oracledb documentation. diff --git a/doc/src/user_guide/plsql_execution.rst b/doc/src/user_guide/plsql_execution.rst index 7a1306ae..d83eef86 100644 --- a/doc/src/user_guide/plsql_execution.rst +++ b/doc/src/user_guide/plsql_execution.rst @@ -4,378 +4,7 @@ PL/SQL Execution **************** -PL/SQL stored procedures, functions and anonymous blocks can be called from -cx_Oracle. +.. include:: ../note.rst -.. _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 ` or -an :ref:`Object Type `. - -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 -`__. 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 -`__ 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 = ; - -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 / - - -- 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/ - - CREATE EDITION demo; - ALTER USER ENABLE EDITIONS FORCE; - GRANT USE ON EDITION demo to ; - -The ``Discount`` function for the demo edition is as follows: - -.. code-block:: sql - - connect / - - 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 +See `Executing PL/SQL `__ in the python-oracledb documentation. diff --git a/doc/src/user_guide/soda.rst b/doc/src/user_guide/soda.rst index 490cafcd..2db1e575 100644 --- a/doc/src/user_guide/soda.rst +++ b/doc/src/user_guide/soda.rst @@ -4,206 +4,8 @@ Simple Oracle Document Access (SODA) ************************************ -Overview -======== +.. include:: ../note.rst -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 -`__, `Java -`__, -`PL/SQL `__, -`Oracle Call Interface -`__ -and via `REST -`__. - -For general information on SODA, see the `SODA home page -`__ -and the Oracle Database `Introduction to Simple Oracle Document Access (SODA) -`__ manual. - -For specific requirements see the cx_Oracle :ref:`SODA requirements `. - -cx_Oracle uses the following objects for SODA: - -* :ref:`SODA Database Object `: 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 `: 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 `: 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 ` for - the forms of SodaDoc. - -* :ref:`SODA Document Cursor `: 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 `: 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 -`__ -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 -` 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 ` 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. +See `Working with Simple Oracle Document Access (SODA) `__ in the +python-oracledb documentation. diff --git a/doc/src/user_guide/sql_execution.rst b/doc/src/user_guide/sql_execution.rst index dca4179e..c7ffeda2 100644 --- a/doc/src/user_guide/sql_execution.rst +++ b/doc/src/user_guide/sql_execution.rst @@ -4,802 +4,7 @@ SQL Execution ************* -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 -`__ can also be executed. +.. include:: ../note.rst -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 -`__ - -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 ` to Python types that can be -optionally :ref:`overridden `. - -.. 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 ` 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', , 39, None, 38, 0, 0) - ('NAME', , 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 `. The last column gives the type of -Python object that is returned by default. Python types can be changed with -:ref:`Output Type Handlers `. - -.. 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 ` - * - 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 ` - * - CHAR - - :attr:`cx_Oracle.DB_TYPE_CHAR` - - str - * - CLOB - - :attr:`cx_Oracle.DB_TYPE_CLOB` - - :ref:`cx_Oracle.LOB ` - * - CURSOR - - :attr:`cx_Oracle.DB_TYPE_CURSOR` - - :ref:`cx_Oracle.Cursor ` - * - 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 ` - * - 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 ` - * - 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 -` or on the :attr:`cursor -`. 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 ` (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 -`__ - -.. _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 -`__ - - -.. _outconverters: - -Changing Query Results with Outconverters ------------------------------------------ - -cx_Oracle "outconverters" can be used with :ref:`output type handlers -` 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 -` 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 -`__, -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 -`. - -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 ` 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 ` for the upper and lower limit -values. - -.. _crc: - -Client Result Cache -------------------- - -Python cx_Oracle applications can use Oracle Database's `Client Result Cache -`__ -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 `, -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 -`__ -``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 ` -or :ref:`sqlnet.ora ` file on the Python host, see `Client -Configuration Parameters -`__. - -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 ` is correct. Review the - :ref:`client and database character sets `. 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() -`__ used by -cx_Oracle to allow it to be selected and prevent the whole query failing. Do -this by creating an :ref:`outputtypehandler ` 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 -`__. - -.. _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 ` 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]) +See `Executing SQL `__ in the python-oracledb documentation. diff --git a/doc/src/user_guide/startup.rst b/doc/src/user_guide/startup.rst index 527a877f..1fab6856 100644 --- a/doc/src/user_guide/startup.rst +++ b/doc/src/user_guide/startup.rst @@ -4,59 +4,8 @@ Starting and Stopping Oracle Database ************************************* -This chapter covers how to start up and shutdown Oracle Database using -cx_Oracle. +.. include:: ../note.rst -=========================== -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 -`_ -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) +See `Starting and Stopping Oracle Database `__ in the +python-oracledb documentation. diff --git a/doc/src/user_guide/tracing_sql.rst b/doc/src/user_guide/tracing_sql.rst index a1e1d3eb..3bea78a9 100644 --- a/doc/src/user_guide/tracing_sql.rst +++ b/doc/src/user_guide/tracing_sql.rst @@ -4,165 +4,7 @@ Tracing SQL and PL/SQL Statements ********************************* -Subclass Connections -==================== +.. include:: ../note.rst -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 -`__ 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 -` 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 -`__ -or `DBMS_SESSION.SET_IDENTIFIER -`__. 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 `__ -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 - cursor.execute("select * from jobss") - cx_Oracle.DatabaseError: ORA-00942: table or view does not exist - -See `ODPI-C Debugging -`__ for -documentation on ``DPI_DEBUG_LEVEL``. +See `Tracing python-oracledb `__ in the python-oracledb documentation. diff --git a/doc/src/user_guide/tuning.rst b/doc/src/user_guide/tuning.rst index ba3bea8b..7820a4db 100644 --- a/doc/src/user_guide/tuning.rst +++ b/doc/src/user_guide/tuning.rst @@ -4,396 +4,7 @@ Tuning cx_Oracle **************** -Some general tuning tips are: +.. include:: ../note.rst -* Tune your application architecture. - - A general application goal is to reduce the number of :ref:`round-trips - ` 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 `. - - 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 - `__. - - Use :ref:`bind variables ` to avoid statement reparsing. - - Tune :attr:`Cursor.arraysize` and :attr:`Cursor.prefetchrows` for each query, - see :ref:`Tuning Fetch Performance `. - - Do simple optimizations like :ref:`limiting the number of rows ` 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 `. - - Enable :ref:`Client Result Caching ` for small lookup tables. - -* Tune your database. See the `Database Performance Tuning Guide - `__. - -* 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 - `__. - -* 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 ` 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 ` 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 ` 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 ` 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 -` 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 -`__, a general -performance and scalability goal is to minimize `round-trips -`__. - -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 `. -* 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 `. - -Finding the Number of Round-Trips -+++++++++++++++++++++++++++++++++ - -Oracle's `Automatic Workload Repository -`__ -(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 -`__ -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 -`__ -(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 -` 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 -`__. -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 `, -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 -`__ -``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 ` -or :ref:`sqlnet.ora ` file on the Python host, see `Client -Configuration Parameters -`__. - -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 +See `Tuning python-oracledb `__ in the python-oracledb documentation. diff --git a/doc/src/user_guide/txn_management.rst b/doc/src/user_guide/txn_management.rst index 9e8985ae..798123cb 100644 --- a/doc/src/user_guide/txn_management.rst +++ b/doc/src/user_guide/txn_management.rst @@ -4,74 +4,7 @@ Transaction Management ********************** -A database transaction is a grouping of SQL statements that make a logical data -change to the database. +.. include:: ../note.rst -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 ` statements (INSERT, UPDATE etc) are committed as they are -executed. Unlike :meth:`Connection.commit()`, this does not require an -additional :ref:`round-trip ` 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. +See `Managing Transactions `__ in the python-oracledb documentation. diff --git a/doc/src/user_guide/xml_data_type.rst b/doc/src/user_guide/xml_data_type.rst index e0e0a068..dbf3646b 100644 --- a/doc/src/user_guide/xml_data_type.rst +++ b/doc/src/user_guide/xml_data_type.rst @@ -4,65 +4,7 @@ Working with XMLTYPE ******************** -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. +.. include:: ../note.rst -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 = """ - - John Smith - 43 - Professor - Mathematics - """ - 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. +See `Using XMLTYPE Data `__ in the python-oracledb documentation. diff --git a/samples/README.md b/samples/README.md index 81c2286e..ea0bb1c8 100644 --- a/samples/README.md +++ b/samples/README.md @@ -1,47 +1,15 @@ -# cx_Oracle Examples +# Samples -This directory contains samples for [cx_Oracle][6]. Documentation is -[here][7]. A separate tutorial is [here][8]. +**cx_Oracle has a major new release under a new name and homepage +[python-oracledb](https://oracle.github.io/python-oracledb/). New projects +should use python-oracledb instead of the obsolete cx_Oracle driver.** -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-oracledb uses the same Python Database API as cx_Oracle, supports the +feature requirements of frameworks that rely on this API, and has many new +features. - python setup_samples.py +**Python-oracledb samples can be found at +[github.com/oracle/python-oracledb/tree/main/samples](https://github.com/oracle/python-oracledb/tree/main/samples).** - 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 +To upgrade to python-oracledb, see [Upgrading from cx_Oracle 8.3 to +python-oracledb](https://python-oracledb.readthedocs.io/en/latest/user_guide/appendix_c.html#upgrading-from-cx-oracle-8-3-to-python-oracledb). diff --git a/samples/app_context.py b/samples/app_context.py deleted file mode 100644 index 41c411f8..00000000 --- a/samples/app_context.py +++ /dev/null @@ -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) diff --git a/samples/aq_notification.py b/samples/aq_notification.py deleted file mode 100644 index 728d5b26..00000000 --- a/samples/aq_notification.py +++ /dev/null @@ -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) diff --git a/samples/array_dml_rowcounts.py b/samples/array_dml_rowcounts.py deleted file mode 100644 index 335d46c3..00000000 --- a/samples/array_dml_rowcounts.py +++ /dev/null @@ -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.") diff --git a/samples/batch_errors.py b/samples/batch_errors.py deleted file mode 100644 index 8d093bdb..00000000 --- a/samples/batch_errors.py +++ /dev/null @@ -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)) diff --git a/samples/bind_insert.py b/samples/bind_insert.py deleted file mode 100644 index fef635ba..00000000 --- a/samples/bind_insert.py +++ /dev/null @@ -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) diff --git a/samples/bind_query.py b/samples/bind_query.py deleted file mode 100644 index 5b09bc7b..00000000 --- a/samples/bind_query.py +++ /dev/null @@ -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() diff --git a/samples/bulk_aq.py b/samples/bulk_aq.py deleted file mode 100644 index 07320e84..00000000 --- a/samples/bulk_aq.py +++ /dev/null @@ -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.") diff --git a/samples/call_timeout.py b/samples/call_timeout.py deleted file mode 100644 index b665d9f5..00000000 --- a/samples/call_timeout.py +++ /dev/null @@ -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) diff --git a/samples/connection_pool.py b/samples/connection_pool.py deleted file mode 100644 index 348712b5..00000000 --- a/samples/connection_pool.py +++ /dev/null @@ -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!") diff --git a/samples/cqn.py b/samples/cqn.py deleted file mode 100644 index 5a055ca6..00000000 --- a/samples/cqn.py +++ /dev/null @@ -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) diff --git a/samples/cqn2.py b/samples/cqn2.py deleted file mode 100644 index 49fe1a5e..00000000 --- a/samples/cqn2.py +++ /dev/null @@ -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) diff --git a/samples/database_change_notification.py b/samples/database_change_notification.py deleted file mode 100644 index 012fc63e..00000000 --- a/samples/database_change_notification.py +++ /dev/null @@ -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) diff --git a/samples/database_shutdown.py b/samples/database_shutdown.py deleted file mode 100644 index 63210b5c..00000000 --- a/samples/database_shutdown.py +++ /dev/null @@ -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) diff --git a/samples/database_startup.py b/samples/database_startup.py deleted file mode 100644 index e0364585..00000000 --- a/samples/database_startup.py +++ /dev/null @@ -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") diff --git a/samples/dbms_output.py b/samples/dbms_output.py deleted file mode 100644 index e16ad0d9..00000000 --- a/samples/dbms_output.py +++ /dev/null @@ -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 diff --git a/samples/dml_returning_multiple_rows.py b/samples/dml_returning_multiple_rows.py deleted file mode 100644 index cd11c2db..00000000 --- a/samples/dml_returning_multiple_rows.py +++ /dev/null @@ -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])) diff --git a/samples/drcp.py b/samples/drcp.py deleted file mode 100644 index 289b8560..00000000 --- a/samples/drcp.py +++ /dev/null @@ -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) diff --git a/samples/drop_samples.py b/samples/drop_samples.py deleted file mode 100644 index d3d51c78..00000000 --- a/samples/drop_samples.py +++ /dev/null @@ -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.") diff --git a/samples/editioning.py b/samples/editioning.py deleted file mode 100644 index 6492c7df..00000000 --- a/samples/editioning.py +++ /dev/null @@ -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)) diff --git a/samples/generic_row_factory.py b/samples/generic_row_factory.py deleted file mode 100644 index 2608b118..00000000 --- a/samples/generic_row_factory.py +++ /dev/null @@ -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() diff --git a/samples/implicit_results.py b/samples/implicit_results.py deleted file mode 100644 index 78b70090..00000000 --- a/samples/implicit_results.py +++ /dev/null @@ -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() diff --git a/samples/insert_geometry.py b/samples/insert_geometry.py deleted file mode 100644 index 9df612fa..00000000 --- a/samples/insert_geometry.py +++ /dev/null @@ -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!") diff --git a/samples/json_blob.py b/samples/json_blob.py deleted file mode 100644 index 2c3df37d..00000000 --- a/samples/json_blob.py +++ /dev/null @@ -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) diff --git a/samples/json_direct.py b/samples/json_direct.py deleted file mode 100644 index a5bcad9c..00000000 --- a/samples/json_direct.py +++ /dev/null @@ -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) diff --git a/samples/last_rowid.py b/samples/last_rowid.py deleted file mode 100644 index 948166b7..00000000 --- a/samples/last_rowid.py +++ /dev/null @@ -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() diff --git a/samples/multi_consumer_aq.py b/samples/multi_consumer_aq.py deleted file mode 100644 index a03d66ed..00000000 --- a/samples/multi_consumer_aq.py +++ /dev/null @@ -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.") diff --git a/samples/object_aq.py b/samples/object_aq.py deleted file mode 100644 index 268e5a21..00000000 --- a/samples/object_aq.py +++ /dev/null @@ -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.") diff --git a/samples/plsql_collection.py b/samples/plsql_collection.py deleted file mode 100644 index 46997200..00000000 --- a/samples/plsql_collection.py +++ /dev/null @@ -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() diff --git a/samples/plsql_function.py b/samples/plsql_function.py deleted file mode 100644 index b67cf4f4..00000000 --- a/samples/plsql_function.py +++ /dev/null @@ -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) diff --git a/samples/plsql_procedure.py b/samples/plsql_procedure.py deleted file mode 100644 index 113d880b..00000000 --- a/samples/plsql_procedure.py +++ /dev/null @@ -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()) diff --git a/samples/plsql_record.py b/samples/plsql_record.py deleted file mode 100644 index 81e3ebcb..00000000 --- a/samples/plsql_record.py +++ /dev/null @@ -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() diff --git a/samples/query.py b/samples/query.py deleted file mode 100644 index d96299c8..00000000 --- a/samples/query.py +++ /dev/null @@ -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) diff --git a/samples/query_arraysize.py b/samples/query_arraysize.py deleted file mode 100644 index 661ddbf5..00000000 --- a/samples/query_arraysize.py +++ /dev/null @@ -1,30 +0,0 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2016, 2021, Oracle and/or its affiliates. All rights reserved. -#------------------------------------------------------------------------------ - -#------------------------------------------------------------------------------ -# query_arraysize.py -# -# Demonstrate how to alter the array size and prefetch rows value on a cursor -# in order to reduce the number of network round trips and overhead required to -# fetch all of the rows from a large table. -#------------------------------------------------------------------------------ - -import time - -import cx_Oracle as oracledb -import sample_env - -connection = oracledb.connect(sample_env.get_main_connect_string()) - -start = time.time() - -cursor = connection.cursor() -cursor.prefetchrows = 1000 -cursor.arraysize = 1000 -cursor.execute('select * from bigtab') -res = cursor.fetchall() -# print(res) # uncomment to display the query results - -elapsed = (time.time() - start) -print("Retrieved", len(res), "rows in", elapsed, "seconds") diff --git a/samples/query_strings_as_bytes.py b/samples/query_strings_as_bytes.py deleted file mode 100644 index 51b7e43e..00000000 --- a/samples/query_strings_as_bytes.py +++ /dev/null @@ -1,49 +0,0 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. -#------------------------------------------------------------------------------ - -#------------------------------------------------------------------------------ -# query_strings_as_bytes.py -# -# Demonstrates how to query strings as bytes (bypassing decoding of the bytes -# into a Python string). This can be useful when attempting to fetch data that -# was stored in the database in the wrong encoding. -# -# This script requires cx_Oracle 8.2 and higher. -#------------------------------------------------------------------------------ - -import cx_Oracle as oracledb -import sample_env - -STRING_VAL = 'I bought a cafetière on the Champs-Élysées' - -def return_strings_as_bytes(cursor, name, default_type, size, precision, - scale): - if default_type == oracledb.DB_TYPE_VARCHAR: - return cursor.var(str, arraysize=cursor.arraysize, bypass_decode=True) - -with oracledb.connect(sample_env.get_main_connect_string()) as conn: - - # truncate table and populate with our data of choice - with conn.cursor() as cursor: - cursor.execute("truncate table TestTempTable") - cursor.execute("insert into TestTempTable values (1, :val)", - val=STRING_VAL) - conn.commit() - - # fetch the data normally and show that it is returned as a string - with conn.cursor() as cursor: - cursor.execute("select IntCol, StringCol from TestTempTable") - print("Data fetched using normal technique:") - for row in cursor: - print(row) - print() - - # fetch the data, bypassing the decode and show that it is returned as - # bytes - with conn.cursor() as cursor: - cursor.outputtypehandler = return_strings_as_bytes - cursor.execute("select IntCol, StringCol from TestTempTable") - print("Data fetched using bypass decode technique:") - for row in cursor: - print(row) diff --git a/samples/raw_aq.py b/samples/raw_aq.py deleted file mode 100644 index f070ed9e..00000000 --- a/samples/raw_aq.py +++ /dev/null @@ -1,58 +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. -#------------------------------------------------------------------------------ - -#------------------------------------------------------------------------------ -# raw_aq.py -# This script demonstrates how to use advanced queuing with RAW data. 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 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...") -for data in PAYLOAD_DATA: - print(data) - queue.enqone(connection.msgproperties(payload=data)) -connection.commit() - -# dequeue the messages -print("\nDequeuing messages...") -while True: - props = queue.deqone() - if not props: - break - print(props.payload.decode()) -connection.commit() -print("\nDone.") diff --git a/samples/ref_cursor.py b/samples/ref_cursor.py deleted file mode 100644 index eed4efff..00000000 --- a/samples/ref_cursor.py +++ /dev/null @@ -1,55 +0,0 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2018, 2021, Oracle and/or its affiliates. All rights reserved. -#------------------------------------------------------------------------------ - -#------------------------------------------------------------------------------ -# ref_cursor.py -# Demonstrates the use of REF cursors. -#------------------------------------------------------------------------------ - -import cx_Oracle as oracledb -import sample_env - -connection = oracledb.connect(sample_env.get_main_connect_string()) -cursor = connection.cursor() - -ref_cursor = connection.cursor() -cursor.callproc("myrefcursorproc", (2, 6, ref_cursor)) -print("Rows between 2 and 6:") -for row in ref_cursor: - print(row) -print() - -ref_cursor = connection.cursor() -cursor.callproc("myrefcursorproc", (8, 9, ref_cursor)) -print("Rows between 8 and 9:") -for row in ref_cursor: - print(row) -print() - -#------------------------------------------------------------------------------ -# Setting prefetchrows and arraysize of a REF cursor can improve performance -# when fetching a large number of rows (Tuned Fetch) -#------------------------------------------------------------------------------ - -# Truncate the table used for this demo -cursor.execute("truncate table TestTempTable") - -# Populate the table with a large number of rows -num_rows = 50000 -sql = "insert into TestTempTable (IntCol) values (:1)" -data = [(n + 1,) for n in range(num_rows)] -cursor.executemany(sql, data) - -# 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("myrefcursorproc2", [ref_cursor]) -print("Sum of IntCol for", num_rows, "rows:") -for row in ref_cursor: - sum_rows += row[0] -print(sum_rows) diff --git a/samples/return_lobs_as_strings.py b/samples/return_lobs_as_strings.py deleted file mode 100644 index 1c420692..00000000 --- a/samples/return_lobs_as_strings.py +++ /dev/null @@ -1,71 +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. -#------------------------------------------------------------------------------ - -#------------------------------------------------------------------------------ -# return_lobs_as_strings.py -# Returns all CLOB values as strings and BLOB values as bytes. The -# performance of this technique is significantly better than fetching the LOBs -# and then reading the contents of the LOBs as it avoids round-trips to the -# database. Be aware, however, that this method requires contiguous memory so -# is not usable for very large LOBs. -# -# This script requires cx_Oracle 5.0 and higher. -#------------------------------------------------------------------------------ - -import cx_Oracle as oracledb -import sample_env - -def output_type_handler(cursor, name, default_type, size, precision, scale): - if default_type == oracledb.CLOB: - return cursor.var(oracledb.LONG_STRING, arraysize=cursor.arraysize) - if default_type == oracledb.BLOB: - return cursor.var(oracledb.LONG_BINARY, arraysize=cursor.arraysize) - -connection = oracledb.connect(sample_env.get_main_connect_string()) -connection.outputtypehandler = output_type_handler -cursor = connection.cursor() - -# add some data to the tables -print("Populating tables with data...") -cursor.execute("truncate table TestClobs") -cursor.execute("truncate table TestBlobs") -long_string = "" -for i in range(10): - char = chr(ord('A') + i) - long_string += char * 25000 - # uncomment the line below for cx_Oracle 5.3 and earlier - # cursor.setinputsizes(None, oracledb.LONG_STRING) - cursor.execute("insert into TestClobs values (:1, :2)", - (i + 1, "STRING " + long_string)) - # uncomment the line below for cx_Oracle 5.3 and earlier - # cursor.setinputsizes(None, oracledb.LONG_BINARY) - cursor.execute("insert into TestBlobs values (:1, :2)", - (i + 1, long_string.encode("ascii"))) -connection.commit() - -# fetch the data and show the results -print("CLOBS returned as strings") -cursor.execute(""" - select - IntCol, - ClobCol - from TestClobs - order by IntCol""") -for int_col, value in cursor: - print("Row:", int_col, "string of length", len(value)) -print() -print("BLOBS returned as bytes") -cursor.execute(""" - select - IntCol, - BlobCol - from TestBlobs - order by IntCol""") -for int_col, value in cursor: - print("Row:", int_col, "string of length", value and len(value) or 0) diff --git a/samples/return_numbers_as_decimals.py b/samples/return_numbers_as_decimals.py deleted file mode 100644 index becb9728..00000000 --- a/samples/return_numbers_as_decimals.py +++ /dev/null @@ -1,31 +0,0 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. -#------------------------------------------------------------------------------ - -#------------------------------------------------------------------------------ -# return_numbers_as_decimals.py -# Returns all numbers as decimals by means of an output type handler. This is -# needed if the full decimal precision of Oracle numbers is required by the -# application. See this article -# (http://blog.reverberate.org/2016/02/06/floating-point-demystified-part2.html) -# for an explanation of why decimal numbers (like Oracle numbers) cannot be -# represented exactly by floating point numbers. -# -# This script requires cx_Oracle 5.0 and higher. -#------------------------------------------------------------------------------ - -import decimal - -import cx_Oracle as oracledb -import sample_env - -def output_type_handler(cursor, name, default_type, size, precision, scale): - if default_type == oracledb.NUMBER: - return cursor.var(decimal.Decimal, arraysize=cursor.arraysize) - -connection = oracledb.connect(sample_env.get_main_connect_string()) -connection.outputtypehandler = output_type_handler -cursor = connection.cursor() -cursor.execute("select * from TestNumbers") -for row in cursor: - print("Row:", row) diff --git a/samples/rows_as_instance.py b/samples/rows_as_instance.py deleted file mode 100644 index 1ab1c631..00000000 --- a/samples/rows_as_instance.py +++ /dev/null @@ -1,57 +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. -#------------------------------------------------------------------------------ - -#------------------------------------------------------------------------------ -# rows_as_instance.py -# Returns rows as instances instead of tuples. See the ceDatabase.Row class -# in the cx_PyGenLib project (http://cx-pygenlib.sourceforge.net) for a more -# advanced example. -# -# This script requires cx_Oracle 4.3 and higher. -#------------------------------------------------------------------------------ - -import cx_Oracle as oracledb -import sample_env - -class Test: - - def __init__(self, a, b, c): - self.a = a - self.b = b - self.c = c - -connection = oracledb.connect(sample_env.get_main_connect_string()) -cursor = connection.cursor() - -# change this to False if you want to create the table yourself using SQL*Plus -# and then populate it with the data of your choice -if True: - cursor.execute(""" - select count(*) - from user_tables - where table_name = 'TESTINSTANCES'""") - count, = cursor.fetchone() - if count: - cursor.execute("drop table TestInstances") - cursor.execute(""" - create table TestInstances ( - a varchar2(60) not null, - b number(9) not null, - c date not null - )""") - cursor.execute("insert into TestInstances values ('First', 5, sysdate)") - cursor.execute("insert into TestInstances values ('Second', 25, sysdate)") - connection.commit() - -# retrieve the data and display it -cursor.execute("select * from TestInstances") -cursor.rowfactory = Test -print("Rows:") -for row in cursor: - print("a = %s, b = %s, c = %s" % (row.a, row.b, row.c)) diff --git a/samples/sample_env.py b/samples/sample_env.py deleted file mode 100644 index 42484d3f..00000000 --- a/samples/sample_env.py +++ /dev/null @@ -1,151 +0,0 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved. -#------------------------------------------------------------------------------ - -#------------------------------------------------------------------------------ -# Sets the environment used by the sample scripts. Production applications -# should consider using External Authentication to avoid hard coded -# credentials. -# -# You can set values in environment variables to bypass the sample requesting -# the information it requires. -# -# CX_ORACLE_SAMPLES_MAIN_USER: user used for most samples -# CX_ORACLE_SAMPLES_MAIN_PASSWORD: password of user used for most samples -# CX_ORACLE_SAMPLES_EDITION_USER: user for editioning -# CX_ORACLE_SAMPLES_EDITION_PASSWORD: password of user for editioning -# CX_ORACLE_SAMPLES_EDITION_NAME: name of edition for editioning -# CX_ORACLE_SAMPLES_CONNECT_STRING: connect string -# CX_ORACLE_SAMPLES_DRCP_CONNECT_STRING: DRCP connect string -# CX_ORACLE_SAMPLES_ADMIN_USER: admin user for setting up samples -# CX_ORACLE_SAMPLES_ADMIN_PASSWORD: admin password for setting up samples -# -# CX_ORACLE_SAMPLES_CONNECT_STRING can be set to an Easy Connect string, or a -# Net Service Name from a tnsnames.ora file or external naming service, -# or it can be the name of a local Oracle database instance. -# -# If using Instant Client, then an Easy Connect string is generally -# appropriate. The syntax is: -# -# [//]host_name[:port][/service_name][:server_type][/instance_name] -# -# Commonly just the host_name and service_name are needed -# e.g. "localhost/orclpdb1" or "localhost/XEPDB1" -# -# If using a tnsnames.ora file, the file can be in a default -# location such as $ORACLE_HOME/network/admin/tnsnames.ora or -# /etc/tnsnames.ora. Alternatively set the TNS_ADMIN environment -# variable and put the file in $TNS_ADMIN/tnsnames.ora. -# -# The administrative user for cloud databases is ADMIN and the administrative -# user for on premises databases is SYSTEM. -#------------------------------------------------------------------------------ - -import getpass -import os -import sys - -# default values -DEFAULT_MAIN_USER = "pythondemo" -DEFAULT_EDITION_USER = "pythoneditions" -DEFAULT_EDITION_NAME = "python_e1" -DEFAULT_CONNECT_STRING = "localhost/orclpdb1" -DEFAULT_DRCP_CONNECT_STRING = "localhost/orclpdb1:pooled" - -# dictionary containing all parameters; these are acquired as needed by the -# methods below (which should be used instead of consulting this dictionary -# directly) and then stored so that a value is not requested more than once -PARAMETERS = {} - -def get_value(name, label, default_value=""): - value = PARAMETERS.get(name) - if value is not None: - return value - env_name = "CX_ORACLE_SAMPLES_" + name - value = os.environ.get(env_name) - if value is None: - if default_value: - label += " [%s]" % default_value - label += ": " - if default_value: - value = input(label).strip() - else: - value = getpass.getpass(label) - if not value: - value = default_value - PARAMETERS[name] = value - return value - -def get_main_user(): - return get_value("MAIN_USER", "Main User Name", DEFAULT_MAIN_USER) - -def get_main_password(): - return get_value("MAIN_PASSWORD", "Password for %s" % get_main_user()) - -def get_edition_user(): - return get_value("EDITION_USER", "Edition User Name", DEFAULT_EDITION_USER) - -def get_edition_password(): - return get_value("EDITION_PASSWORD", - "Password for %s" % get_edition_user()) - -def get_edition_name(): - return get_value("EDITION_NAME", "Edition Name", DEFAULT_EDITION_NAME) - -def get_connect_string(): - return get_value("CONNECT_STRING", "Connect String", - DEFAULT_CONNECT_STRING) - -def get_main_connect_string(password=None): - if password is None: - password = get_main_password() - return "%s/%s@%s" % (get_main_user(), password, get_connect_string()) - -def get_drcp_connect_string(): - connect_string = get_value("DRCP_CONNECT_STRING", "DRCP Connect String", - DEFAULT_DRCP_CONNECT_STRING) - return "%s/%s@%s" % (get_main_user(), get_main_password(), connect_string) - -def get_edition_connect_string(): - return "%s/%s@%s" % \ - (get_edition_user(), get_edition_password(), get_connect_string()) - -def get_admin_connect_string(): - admin_user = get_value("ADMIN_USER", "Administrative user", "admin") - admin_password = get_value("ADMIN_PASSWORD", "Password for %s" % admin_user) - return "%s/%s@%s" % (admin_user, admin_password, get_connect_string()) - -def run_sql_script(conn, script_name, **kwargs): - statement_parts = [] - cursor = conn.cursor() - replace_values = [("&" + k + ".", v) for k, v in kwargs.items()] + \ - [("&" + k, v) for k, v in kwargs.items()] - script_dir = os.path.dirname(os.path.abspath(sys.argv[0])) - file_name = os.path.join(script_dir, "sql", script_name + "_exec.sql") - for line in open(file_name): - if line.strip() == "/": - statement = "".join(statement_parts).strip() - if statement: - for search_value, replace_value in replace_values: - statement = statement.replace(search_value, replace_value) - try: - cursor.execute(statement) - except: - print("Failed to execute SQL:", statement) - raise - statement_parts = [] - else: - statement_parts.append(line) - cursor.execute(""" - select name, type, line, position, text - from dba_errors - where owner = upper(:owner) - order by name, type, line, position""", - owner = get_main_user()) - prev_name = prev_obj_type = None - for name, obj_type, line_num, position, text in cursor: - if name != prev_name or obj_type != prev_obj_type: - print("%s (%s)" % (name, obj_type)) - prev_name = name - prev_obj_type = obj_type - print(" %s/%s %s" % (line_num, position, text)) diff --git a/samples/scrollable_cursors.py b/samples/scrollable_cursors.py deleted file mode 100644 index 3fb855b1..00000000 --- a/samples/scrollable_cursors.py +++ /dev/null @@ -1,70 +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. -#------------------------------------------------------------------------------ - -#------------------------------------------------------------------------------ -# scrollable_cursors.py -# This script demonstrates how to use scrollable cursors. These allow moving -# forward and backward in the result set but incur additional overhead on the -# server to retain this information. -# -# 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()) - -# show all of the rows available in the table -cursor = connection.cursor() -cursor.execute("select * from TestStrings order by IntCol") -print("ALL ROWS") -for row in cursor: - print(row) -print() - -# create a scrollable cursor -cursor = connection.cursor(scrollable = True) - -# set array size smaller than the default (100) to force scrolling by the -# database; otherwise, scrolling occurs directly within the buffers -cursor.arraysize = 3 -cursor.execute("select * from TestStrings order by IntCol") - -# scroll to last row in the result set; the first parameter is not needed and -# is ignored) -cursor.scroll(mode = "last") -print("LAST ROW") -print(cursor.fetchone()) -print() - -# scroll to the first row in the result set; the first parameter not needed and -# is ignored -cursor.scroll(mode = "first") -print("FIRST ROW") -print(cursor.fetchone()) -print() - -# scroll to an absolute row number -cursor.scroll(5, mode = "absolute") -print("ROW 5") -print(cursor.fetchone()) -print() - -# scroll forward six rows (the mode parameter defaults to relative) -cursor.scroll(3) -print("SKIP 3 ROWS") -print(cursor.fetchone()) -print() - -# scroll backward four rows (the mode parameter defaults to relative) -cursor.scroll(-4) -print("SKIP BACK 4 ROWS") -print(cursor.fetchone()) -print() diff --git a/samples/session_callback.py b/samples/session_callback.py deleted file mode 100644 index a1507fc8..00000000 --- a/samples/session_callback.py +++ /dev/null @@ -1,138 +0,0 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2019, 2021, Oracle and/or its affiliates. All rights reserved. -#------------------------------------------------------------------------------ - -#------------------------------------------------------------------------------ -# session_callback.py -# -# Demonstrate how to use a connection pool session callback written in -# Python. The callback is invoked whenever a newly created session is acquired -# from the pool, or when the requested tag does not match the tag that is -# associated with the session. It is generally used to set session state, so -# that the application can count on known session state, which allows the -# application to reduce the number of round trips made to the database. -# If all your connections should have the same session state, you can simplify -# the session callback by removing the tagging logic. -# -# This script requires cx_Oracle 7.1 or higher. -# -# Also see session_callback_plsql.py -# -#------------------------------------------------------------------------------ - -import cx_Oracle as oracledb -import sample_env - -# define a dictionary of NLS_DATE_FORMAT formats supported by this sample -SUPPORTED_FORMATS = { - "SIMPLE" : "'YYYY-MM-DD HH24:MI'", - "FULL" : "'YYYY-MM-DD HH24:MI:SS'" -} - -# define a dictionary of TIME_ZONE values supported by this sample -SUPPORTED_TIME_ZONES = { - "UTC" : "'UTC'", - "MST" : "'-07:00'" -} - -# define a dictionary of keys that are supported by this sample -SUPPORTED_KEYS = { - "NLS_DATE_FORMAT" : SUPPORTED_FORMATS, - "TIME_ZONE" : SUPPORTED_TIME_ZONES -} - -# define session callback -def init_session(conn, requested_tag): - - # display the requested and actual tags - print("init_session(): requested tag=%r, actual tag=%r" % \ - (requested_tag, conn.tag)) - - # tags are expected to be in the form "key1=value1;key2=value2" - # in this example, they are used to set NLS parameters and the tag is - # parsed to validate it - if requested_tag is not None: - state_parts = [] - for directive in requested_tag.split(";"): - parts = directive.split("=") - if len(parts) != 2: - raise ValueError("Tag must contain key=value pairs") - key, value = parts - value_dict = SUPPORTED_KEYS.get(key) - if value_dict is None: - raise ValueError("Tag only supports keys: %s" % \ - (", ".join(SUPPORTED_KEYS))) - actual_value = value_dict.get(value) - if actual_value is None: - raise ValueError("Key %s only supports values: %s" % \ - (key, ", ".join(value_dict))) - state_parts.append("%s = %s" % (key, actual_value)) - sql = "alter session set %s" % " ".join(state_parts) - cursor = conn.cursor() - cursor.execute(sql) - - # assign the requested tag to the connection so that when the connection - # is closed, it will automatically be retagged; note that if the requested - # tag is None (no tag was requested) this has no effect - conn.tag = requested_tag - - -# create pool with session callback defined -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, session_callback=init_session) - -# acquire session without specifying a tag; since the session returned is -# newly created, the callback will be invoked but since there is no tag -# specified, no session state will be changed -print("(1) acquire session without tag") -with pool.acquire() as conn: - cursor = conn.cursor() - cursor.execute("select to_char(current_date) from dual") - result, = cursor.fetchone() - print("main(): result is", repr(result)) - -# acquire session, specifying a tag; since the session returned has no tag, -# the callback will be invoked; session state will be changed and the tag will -# be saved when the connection is closed -print("(2) acquire session with tag") -with pool.acquire(tag="NLS_DATE_FORMAT=SIMPLE") as conn: - cursor = conn.cursor() - cursor.execute("select to_char(current_date) from dual") - result, = cursor.fetchone() - print("main(): result is", repr(result)) - -# acquire session, specifying the same tag; since a session exists in the pool -# with this tag, it will be returned and the callback will not be invoked but -# the connection will still have the session state defined previously -print("(3) acquire session with same tag") -with pool.acquire(tag="NLS_DATE_FORMAT=SIMPLE") as conn: - cursor = conn.cursor() - cursor.execute("select to_char(current_date) from dual") - result, = cursor.fetchone() - print("main(): result is", repr(result)) - -# acquire session, specifying a different tag; since no session exists in the -# pool with this tag, a new session will be returned and the callback will be -# invoked; session state will be changed and the tag will be saved when the -# connection is closed -print("(4) acquire session with different tag") -with pool.acquire(tag="NLS_DATE_FORMAT=FULL;TIME_ZONE=UTC") as conn: - cursor = conn.cursor() - cursor.execute("select to_char(current_date) from dual") - result, = cursor.fetchone() - print("main(): result is", repr(result)) - -# acquire session, specifying a different tag but also specifying that a -# session with any tag can be acquired from the pool; a session with one of the -# previously set tags will be returned and the callback will be invoked; -# session state will be changed and the tag will be saved when the connection -# is closed -print("(4) acquire session with different tag but match any also specified") -with pool.acquire(tag="NLS_DATE_FORMAT=FULL;TIME_ZONE=MST", matchanytag=True) \ - as conn: - cursor = conn.cursor() - cursor.execute("select to_char(current_date) from dual") - result, = cursor.fetchone() - print("main(): result is", repr(result)) diff --git a/samples/session_callback_plsql.py b/samples/session_callback_plsql.py deleted file mode 100644 index a25486f8..00000000 --- a/samples/session_callback_plsql.py +++ /dev/null @@ -1,102 +0,0 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2019, 2021, Oracle and/or its affiliates. All rights reserved. -#------------------------------------------------------------------------------ - -#------------------------------------------------------------------------------ -# session_callback_plsql.py -# -# Demonstrate how to use a connection pool session callback written in -# PL/SQL. The callback is invoked whenever the tag requested by the application -# does not match the tag associated with the session in the pool. It should be -# used to set session state, so that the application can count on known session -# state, which allows the application to reduce the number of round trips to the -# database. -# -# The primary advantage to this approach over the equivalent approach shown in -# session_callback.py is when DRCP is used, as the callback is invoked on the -# server and no round trip is required to set state. -# -# This script requires cx_Oracle 7.1 or higher. -# -# Also see session_callback.py -# -#------------------------------------------------------------------------------ - -import cx_Oracle as oracledb -import sample_env - -# create pool with session callback defined -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, - session_callback="pkg_SessionCallback.TheCallback") - -# truncate table logging calls to PL/SQL session callback -with pool.acquire() as conn: - cursor = conn.cursor() - cursor.execute("truncate table PLSQLSessionCallbacks") - -# acquire session without specifying a tag; the callback will not be invoked as -# a result and no session state will be changed -print("(1) acquire session without tag") -with pool.acquire() as conn: - cursor = conn.cursor() - cursor.execute("select to_char(current_date) from dual") - result, = cursor.fetchone() - print("main(): result is", repr(result)) - -# acquire session, specifying a tag; since the session returned has no tag, -# the callback will be invoked; session state will be changed and the tag will -# be saved when the connection is closed -print("(2) acquire session with tag") -with pool.acquire(tag="NLS_DATE_FORMAT=SIMPLE") as conn: - cursor = conn.cursor() - cursor.execute("select to_char(current_date) from dual") - result, = cursor.fetchone() - print("main(): result is", repr(result)) - -# acquire session, specifying the same tag; since a session exists in the pool -# with this tag, it will be returned and the callback will not be invoked but -# the connection will still have the session state defined previously -print("(3) acquire session with same tag") -with pool.acquire(tag="NLS_DATE_FORMAT=SIMPLE") as conn: - cursor = conn.cursor() - cursor.execute("select to_char(current_date) from dual") - result, = cursor.fetchone() - print("main(): result is", repr(result)) - -# acquire session, specifying a different tag; since no session exists in the -# pool with this tag, a new session will be returned and the callback will be -# invoked; session state will be changed and the tag will be saved when the -# connection is closed -print("(4) acquire session with different tag") -with pool.acquire(tag="NLS_DATE_FORMAT=FULL;TIME_ZONE=UTC") as conn: - cursor = conn.cursor() - cursor.execute("select to_char(current_date) from dual") - result, = cursor.fetchone() - print("main(): result is", repr(result)) - -# acquire session, specifying a different tag but also specifying that a -# session with any tag can be acquired from the pool; a session with one of the -# previously set tags will be returned and the callback will be invoked; -# session state will be changed and the tag will be saved when the connection -# is closed -print("(4) acquire session with different tag but match any also specified") -with pool.acquire(tag="NLS_DATE_FORMAT=FULL;TIME_ZONE=MST", matchanytag=True) \ - as conn: - cursor = conn.cursor() - cursor.execute("select to_char(current_date) from dual") - result, = cursor.fetchone() - print("main(): result is", repr(result)) - -# acquire session and display results from PL/SQL session logs -with pool.acquire() as conn: - cursor = conn.cursor() - cursor.execute(""" - select RequestedTag, ActualTag - from PLSQLSessionCallbacks - order by FixupTimestamp""") - print("(5) PL/SQL session callbacks") - for requestedTag, actualTag in cursor: - print("Requested:", requestedTag, "Actual:", actualTag) diff --git a/samples/setup_samples.py b/samples/setup_samples.py deleted file mode 100644 index 0925c135..00000000 --- a/samples/setup_samples.py +++ /dev/null @@ -1,32 +0,0 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2019, 2021, Oracle and/or its affiliates. All rights reserved. -#------------------------------------------------------------------------------ - -#------------------------------------------------------------------------------ -# setup_samples.py -# -# Creates users and populates their schemas with the tables and packages -# necessary for running the sample scripts. An edition is also created for the -# demonstration of PL/SQL editioning. -#------------------------------------------------------------------------------ - -import cx_Oracle as oracledb - -import sample_env -import drop_samples - -# connect as administrative user (usually SYSTEM or ADMIN) -conn = oracledb.connect(sample_env.get_admin_connect_string()) - -# drop existing users and editions, if applicable -drop_samples.drop_samples(conn) - -# create sample schema and edition -print("Creating sample schemas and edition...") -sample_env.run_sql_script(conn, "setup_samples", - main_user=sample_env.get_main_user(), - main_password=sample_env.get_main_password(), - edition_user=sample_env.get_edition_user(), - edition_password=sample_env.get_edition_password(), - edition_name=sample_env.get_edition_name()) -print("Done.") diff --git a/samples/sharding_number_key.py b/samples/sharding_number_key.py deleted file mode 100644 index a71ac1a5..00000000 --- a/samples/sharding_number_key.py +++ /dev/null @@ -1,35 +0,0 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved. -#------------------------------------------------------------------------------ - -#------------------------------------------------------------------------------ -# sharding_number_key.py -# This script demonstrates how to use sharding keys with a sharded database. -# The sample schema provided does not include support for running this demo. A -# sharded database must first be created. Information on how to create a -# sharded database can be found in the documentation: -# https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=SHARD -# -# This script requires cx_Oracle 6.1 and higher but it is recommended to use -# cx_Oracle 7.3 and higher in order to avoid a set of known issues when using -# sharding capabilities. -#------------------------------------------------------------------------------ - -import cx_Oracle as oracledb -import sample_env - -pool = oracledb.SessionPool(user=sample_env.get_main_user(), - password=sample_env.get_main_password(), - dsn=sample_env.get_connect_string(), min=1, max=5, - increment=1) - -def connect_and_display(sharding_key): - print("Connecting with sharding key:", sharding_key) - with pool.acquire(shardingkey=[sharding_key]) as conn: - cursor = conn.cursor() - cursor.execute("select sys_context('userenv', 'db_name') from dual") - name, = cursor.fetchone() - print("--> connected to database", name) - -connect_and_display(100) -connect_and_display(167) diff --git a/samples/soda_basic.py b/samples/soda_basic.py deleted file mode 100644 index a04713af..00000000 --- a/samples/soda_basic.py +++ /dev/null @@ -1,120 +0,0 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2018, 2021, Oracle and/or its affiliates. All rights reserved. -#------------------------------------------------------------------------------ - -#------------------------------------------------------------------------------ -# soda_basic.py -# A basic Simple Oracle Document Access (SODA) example. -# -# This script requires cx_Oracle 7.0 and higher. -# Oracle Client must be at 18.3 or higher. -# Oracle Database must be at 18.1 or higher. -# The user must have been granted the SODA_APP privilege. -#------------------------------------------------------------------------------ - -import cx_Oracle as oracledb -import sample_env - -connection = oracledb.connect(sample_env.get_main_connect_string()) - -# The general recommendation for simple SODA usage is to enable autocommit -connection.autocommit = True - -# Create the parent object for SODA -soda = connection.getSodaDatabase() - -# drop the collection if it already exists in order to ensure that the sample -# runs smoothly each time -collection = soda.openCollection("mycollection") -if collection is not None: - collection.drop() - -# Explicit metadata is used for maximum version portability. -# Refer to the documentation. -metadata = { - "keyColumn": { - "name": "ID" - }, - "contentColumn": { - "name": "JSON_DOCUMENT", - "sqlType": "BLOB" - }, - "versionColumn": { - "name": "VERSION", - "method": "UUID" - }, - "lastModifiedColumn": { - "name": "LAST_MODIFIED" - }, - "creationTimeColumn": { - "name": "CREATED_ON" - } -} - -# Create a new SODA collection and index -# This will open an existing collection, if the name is already in use. -collection = soda.createCollection("mycollection", metadata) - -index_spec = { - 'name': 'CITY_IDX', - 'fields': [ - { - 'path': 'address.city', - 'datatype': 'string', - 'order': 'asc' - } - ] -} -collection.createIndex(index_spec) - -# Insert a document. -# A system generated key is created by default. -content = {'name': 'Matilda', 'address': {'city': 'Melbourne'}} -doc = collection.insertOneAndGet(content) -key = doc.key -print('The key of the new SODA document is: ', key) - -# Fetch the document back -doc = collection.find().key(key).getOne() # A SodaDocument -content = doc.getContent() # A JavaScript object -print('Retrieved SODA document dictionary is:') -print(content) -content = doc.getContentAsString() # A JSON string -print('Retrieved SODA document string is:') -print(content) - -# Replace document contents -content = {'name': 'Matilda', 'address': {'city': 'Sydney'}} -collection.find().key(key).replaceOne(content) - -# Insert some more documents without caring about their keys -content = {'name': 'Venkat', 'address': {'city': 'Bengaluru'}} -collection.insertOne(content) -content = {'name': 'May', 'address': {'city': 'London'}} -collection.insertOne(content) -content = {'name': 'Sally-Ann', 'address': {'city': 'San Francisco'}} -collection.insertOne(content) - -# Find all documents with names like 'Ma%' -print("Names matching 'Ma%'") -documents = collection.find().filter({'name': {'$like': 'Ma%'}}).getDocuments() -for d in documents: - content = d.getContent() - print(content["name"]) - -# Count all documents -c = collection.find().count() -print('Collection has', c, 'documents') - -# Remove documents with cities containing 'o' -print('Removing documents') -c = collection.find().filter({'address.city': {'$regex': '.*o.*'}}).remove() -print('Dropped', c, 'documents') - -# Count all documents -c = collection.find().count() -print('Collection has', c, 'documents') - -# Drop the collection -if collection.drop(): - print('Collection was dropped') diff --git a/samples/soda_bulk_insert.py b/samples/soda_bulk_insert.py deleted file mode 100644 index 714276a3..00000000 --- a/samples/soda_bulk_insert.py +++ /dev/null @@ -1,79 +0,0 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2019, 2021, Oracle and/or its affiliates. All rights reserved. -#------------------------------------------------------------------------------ - -#------------------------------------------------------------------------------ -# soda_bulk_insert.py -# Demonstrates the use of SODA bulk insert. -# -# This script requires cx_Oracle 7.2 and higher. -# Oracle Client must be at 18.5 or higher. -# Oracle Database must be at 18.1 or higher. -# The user must have been granted the SODA_APP privilege. -#------------------------------------------------------------------------------ - -import cx_Oracle as oracledb -import sample_env - -connection = oracledb.connect(sample_env.get_main_connect_string()) - -# the general recommendation for simple SODA usage is to enable autocommit -connection.autocommit = True - -# create the parent object for all SODA work -soda = connection.getSodaDatabase() - -# drop the collection if it already exists in order to ensure that the sample -# runs smoothly each time -collection = soda.openCollection("SodaBulkInsert") -if collection is not None: - collection.drop() - -# Explicit metadata is used for maximum version portability. -# Refer to the documentation. -metadata = { - "keyColumn": { - "name": "ID" - }, - "contentColumn": { - "name": "JSON_DOCUMENT", - "sqlType": "BLOB" - }, - "versionColumn": { - "name": "VERSION", - "method": "UUID" - }, - "lastModifiedColumn": { - "name": "LAST_MODIFIED" - }, - "creationTimeColumn": { - "name": "CREATED_ON" - } -} - -# create a new (or open an existing) SODA collection -collection = soda.createCollection("SodaBulkInsert", metadata) - -# remove all documents from the collection -collection.find().remove() - -# define some documents that will be stored -in_docs = [ - dict(name="Sam", age=8), - dict(name="George", age=46), - dict(name="Bill", age=35), - dict(name="Sally", age=43), - dict(name="Jill", age=28), - dict(name="Cynthia", age=12) -] - -# perform bulk insert -result_docs = collection.insertManyAndGet(in_docs) -for doc in result_docs: - print("Inserted SODA document with key", doc.key) -print() - -# perform search of all persons under the age of 40 -print("Persons under the age of 40:") -for doc in collection.find().filter({'age': {'$lt': 40}}).getDocuments(): - print(doc.getContent()["name"] + ",", "key", doc.key) diff --git a/samples/spatial_to_geopandas.py b/samples/spatial_to_geopandas.py deleted file mode 100644 index 1652e128..00000000 --- a/samples/spatial_to_geopandas.py +++ /dev/null @@ -1,186 +0,0 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2018, 2021, Oracle and/or its affiliates. All rights reserved. -#------------------------------------------------------------------------------ - -#------------------------------------------------------------------------------ -# spatial_to_geopandas.py -# GeoPandas is a popular python library for working with geospatial data. -# GeoPandas extends the Pandas data analysis library with geospatial support -# using the Shapely library for geometry object support. -# -# See http://geopandas.org, https://pandas.pydata.org, -# and https://github.com/Toblerity/Shapely. -# -# This example shows how to bring geometries from Oracle Spatial (SDO_GEOMETRY -# data type) into GeoPandas and perform a simple spatial operation. While the -# spatial operation we perform in Python could have been performed in the -# Oracle database, this example targets use cases where Python with GeoPandas -# is being used to combine and work with geospatial data from numerous -# additional sources such as files and web services. -# -# This script requires cx_Oracle (5.3 and higher) as well as GeoPandas and its -# dependencies (see http://geopandas.org/install.html). -#------------------------------------------------------------------------------ - -from shapely.wkb import loads -import geopandas as gpd - -import cx_Oracle as oracledb -import sample_env - -# create Oracle connection and cursor objects -connection = oracledb.connect(sample_env.get_main_connect_string()) -cursor = connection.cursor() - -# enable autocommit to avoid the additional round trip to the database to -# perform a commit; this should not be used if multiple statements must be -# executed for a single transaction -connection.autocommit = True - -# define output type handler to fetch LOBs, avoiding the second round trip to -# the database to read the LOB contents -def output_type_handler(cursor, name, default_type, size, precision, scale): - if default_type == oracledb.BLOB: - return cursor.var(oracledb.LONG_BINARY, arraysize=cursor.arraysize) -connection.outputtypehandler = output_type_handler - -# drop and create table -print("Dropping and creating table...") -cursor.execute(""" - begin - execute immediate 'drop table TestStates'; - exception when others then - if sqlcode <> -942 then - raise; - end if; - end;""") -cursor.execute(""" - create table TestStates ( - state VARCHAR2(30) not null, - geometry SDO_GEOMETRY not null - )""") - -# acquire types used for creating SDO_GEOMETRY objects -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") - -# define function for creating an SDO_GEOMETRY object -def create_geometry_obj(*ordinates): - geometry = type_obj.newobject() - geometry.SDO_GTYPE = 2003 - geometry.SDO_SRID = 8307 - geometry.SDO_ELEM_INFO = element_info_type_obj.newobject() - geometry.SDO_ELEM_INFO.extend([1, 1003, 1]) - geometry.SDO_ORDINATES = ordinate_type_obj.newobject() - geometry.SDO_ORDINATES.extend(ordinates) - return geometry - -# create SDO_GEOMETRY objects for three adjacent states in the USA -geometry_nevada = create_geometry_obj(-114.052025, 37.103989, -114.049797, - 37.000423, -113.484375, 37, -112.898598, 37.000401,-112.539604, - 37.000683, -112, 37.000977, -111.412048, 37.001514, -111.133018, - 37.00079,-110.75, 37.003201, -110.5, 37.004265, -110.469505, 36.998001, - -110, 36.997967, -109.044571,36.999088, -109.045143, 37.375, - -109.042824, 37.484692, -109.040848, 37.881176, -109.041405,38.153027, - -109.041107, 38.1647, -109.059402, 38.275501, -109.059296, 38.5, - -109.058868, 38.719906,-109.051765, 39, -109.050095, 39.366699, - -109.050697, 39.4977, -109.050499, 39.6605, -109.050156,40.222694, - -109.047577, 40.653641, -109.0494, 41.000702, -109.2313, 41.002102, - -109.534233,40.998184, -110, 40.997398, -110.047768, 40.997696, -110.5, - 40.994801, -111.045982, 40.998013,-111.045815, 41.251774, -111.045097, - 41.579899, -111.045944, 42.001633, -111.506493, 41.999588,-112.108742, - 41.997677, -112.16317, 41.996784, -112.172562, 41.996643, -112.192184, - 42.001244,-113, 41.998314, -113.875, 41.988091, -114.040871, 41.993805, - -114.038803, 41.884899, -114.041306,41, -114.04586, 40.116997, - -114.046295, 39.906101, -114.046898, 39.542801, -114.049026, 38.67741, - -114.049339, 38.572968, -114.049095, 38.14864, -114.0476, - 37.80946,-114.05098, 37.746284, -114.051666, 37.604805, -114.052025, - 37.103989) -geometry_wyoming = create_geometry_obj(-111.045815, 41.251774, -111.045982, - 40.998013, -110.5, 40.994801, -110.047768, 40.997696, -110, 40.997398, - -109.534233, 40.998184, -109.2313, 41.002102, -109.0494, 41.000702, - -108.525368, 40.999634, -107.917793, 41.002071, -107.317177, 41.002956, - -106.857178, 41.002697, -106.455704, 41.002167, -106.320587, 40.999153, - -106.189987, 40.997604, -105.729874, 40.996906, -105.276604, 40.998188, - -104.942848, 40.998226, -104.625, 41.00145, -104.052742, 41.001423, - -104.051781, 41.39333, -104.052032, 41.564301, -104.052185, 41.697983, - -104.052109, 42.001736, -104.052277, 42.611626, -104.052643, 43.000614, - -104.054337, 43.47784, -104.054298, 43.503101, -104.055, 43.8535, - -104.054108, 44.141102, -104.054001, 44.180401, -104.055458, 44.570877, - -104.057205, 44.997444, -104.664658, 44.998631, -105.037872, 45.000359, - -105.088867, 45.000462, -105.912819, 45.000957, -105.927612, 44.99366, - -106.024239, 44.993591, -106.263, 44.993801, -107.054871, 44.996384, - -107.133545, 45.000141, -107.911095, 45.001343, -108.248672, 44.999504, - -108.620628, 45.000328, -109.082314, 44.999664, -109.102745, 45.005955, - -109.797951, 45.002247, -110.000771, 45.003502, -110.10936, 45.003967, - -110.198761, 44.99625, -110.286026, 44.99691, -110.361946, 45.000656, - -110.402176, 44.993874, -110.5, 44.992355, -110.704506, 44.99239, - -110.784241, 45.003021, -111.05442, 45.001392, -111.054558, 44.666336, - -111.048203, 44.474144, -111.046272, 43.983456, -111.044724, 43.501213, - -111.043846, 43.3158, -111.043381, 43.02013, -111.042786, 42.719578, - -111.045967, 42.513187, -111.045944, 42.001633, -111.045097, 41.579899, - -111.045815, 41.251774) -geometry_colorado = create_geometry_obj(-109.045143, 37.375, -109.044571, - 36.999088, -108.378571, 36.999516, -107.481133, 37, -107.420311, 37, - -106.876701, 37.00013, -106.869209, 36.992416, -106.475639, 36.993748, - -106.006058, 36.995327, -105.717834, 36.995823, -105.220055, 36.995144, - -105.154488, 36.995239, -105.028671, 36.992702, -104.407616, 36.993446, - -104.007324, 36.996216, -103.085617, 37.000244, -103.001709, 37.000084, - -102.986488, 36.998505, -102.759384, 37, -102.69767, 36.995132, - -102.041794, 36.993061, -102.041191, 37.389172, -102.04113, 37.644268, - -102.041695, 37.738529, -102.043938, 38.262466, -102.044113, 38.268803, - -102.04483, 38.615234, -102.044762, 38.697556, -102.046112, 39.047035, - -102.046707, 39.133144, -102.049301, 39.568176, -102.049347, 39.574062, - -102.051277, 40.00309, -102.051117, 40.34922, -102.051003, 40.440018, - -102.050873, 40.697556, -102.050835, 40.749596, -102.051155, 41.002384, - -102.620567, 41.002609, -102.652992, 41.002342, -103.382011, 41.00227, - -103.574036, 41.001736, -104.052742, 41.001423, -104.625, 41.00145, - -104.942848, 40.998226, -105.276604, 40.998188, -105.729874, 40.996906, - -106.189987, 40.997604, -106.320587, 40.999153, -106.455704, 41.002167, - -106.857178, 41.002697, -107.317177, 41.002956, -107.917793, 41.002071, - -108.525368, 40.999634, -109.0494, 41.000702, -109.047577, 40.653641, - -109.050156, 40.222694, -109.050499, 39.6605, -109.050697, 39.4977, - -109.050095, 39.366699, -109.051765, 39, -109.058868, 38.719906, - -109.059296, 38.5, -109.059402, 38.275501, -109.041107, 38.1647, - -109.041405, 38.153027, -109.040848, 37.881176, -109.042824, 37.484692, - -109.045143, 37.375) - -# Insert rows for test states. If we were analyzing these geometries in Oracle -# we would also add Spatial metadata and indexes. However in this example we -# are only storing the geometries so that we load them back into Python, so we -# will skip the metadata and indexes. -print("Adding rows to table...") -data = [ - ('Nevada', geometry_nevada), - ('Colorado', geometry_colorado), - ('Wyoming', geometry_wyoming) -] -cursor.executemany('insert into TestStates values (:state, :obj)', data) - -# We now have test geometries in Oracle Spatial (SDO_GEOMETRY) and will next -# bring them back into Python to analyze with GeoPandas. GeoPandas is able to -# consume geometries in the Well Known Text (WKT) and Well Known Binary (WKB) -# formats. Oracle database includes utility functions to return SDO_GEOMETRY as -# both WKT and WKB. Therefore we use that utility function in the query below -# to provide results in a format readily consumable by GeoPandas. These utility -# functions were introduced in Oracle 10g. We use WKB here; however the same -# process applies for WKT. -cursor.execute(""" - SELECT state, sdo_util.to_wkbgeometry(geometry) - FROM TestStates""") -gdf = gpd.GeoDataFrame(cursor.fetchall(), columns=['state', 'wkbgeometry']) - -# create GeoSeries to replace the WKB geometry column -gdf['geometry'] = gpd.GeoSeries(gdf['wkbgeometry'].apply(lambda x: loads(x))) -del gdf['wkbgeometry'] - -# display the GeoDataFrame -print() -print(gdf) - -# perform a basic GeoPandas operation (unary_union) -# to combine the 3 adjacent states into 1 geometry -print() -print("GeoPandas combining the 3 geometries into a single geometry...") -print(gdf.unary_union) diff --git a/samples/sql/drop_samples.sql b/samples/sql/drop_samples.sql deleted file mode 100644 index eca66a94..00000000 --- a/samples/sql/drop_samples.sql +++ /dev/null @@ -1,28 +0,0 @@ -/*----------------------------------------------------------------------------- - * Copyright 2017, 2019, Oracle and/or its affiliates. All rights reserved. - *---------------------------------------------------------------------------*/ - -/*----------------------------------------------------------------------------- - * drop_samples.sql - * Drops database objects used for cx_Oracle samples. - * - * Run this like: - * sqlplus sys/syspassword@hostname/servicename as sysdba @drop_samples - *---------------------------------------------------------------------------*/ - -whenever sqlerror exit failure - --- get parameters -set echo off termout on feedback off verify off -accept main_user char default pythondemo - - prompt "Name of main schema [pythondemo]: " -accept edition_user char default pythoneditions - - prompt "Name of edition schema [pythoneditions]: " -accept edition_name char default python_e1 - - prompt "Name of edition [python_e1]: " -set feedback on - --- perform work -@@drop_samples_exec.sql - -exit diff --git a/samples/sql/drop_samples_exec.sql b/samples/sql/drop_samples_exec.sql deleted file mode 100644 index dc0ab7a7..00000000 --- a/samples/sql/drop_samples_exec.sql +++ /dev/null @@ -1,32 +0,0 @@ -/*----------------------------------------------------------------------------- - * Copyright 2017, 2019, Oracle and/or its affiliates. All rights reserved. - *---------------------------------------------------------------------------*/ - -/*----------------------------------------------------------------------------- - * drop_samples_exec.sql - * This script performs the actual work of dropping the database schemas and - * edition used by the cx_Oracle samples. It is called by the drop_samples.sql - * and setup_samples.sql files after acquiring the necessary parameters and - * also by the Python script drop_samples.py. - *---------------------------------------------------------------------------*/ - -begin - - for r in - ( select username - from dba_users - where username in (upper('&main_user'), upper('&edition_user')) - ) loop - execute immediate 'drop user ' || r.username || ' cascade'; - end loop; - - for r in - ( select edition_name - from dba_editions - where edition_name in (upper('&edition_name')) - ) loop - execute immediate 'drop edition ' || r.edition_name || ' cascade'; - end loop; - -end; -/ diff --git a/samples/sql/setup_samples.sql b/samples/sql/setup_samples.sql deleted file mode 100644 index 56c8554e..00000000 --- a/samples/sql/setup_samples.sql +++ /dev/null @@ -1,33 +0,0 @@ -/*----------------------------------------------------------------------------- - * Copyright 2017, 2019, Oracle and/or its affiliates. All rights reserved. - *---------------------------------------------------------------------------*/ - -/*----------------------------------------------------------------------------- - * setup_samples.sql - * Creates and populates schemas with the database objects used by the - * cx_Oracle samples. An edition is also created for the demonstration of - * PL/SQL editioning. - * - * Run this like: - * sqlplus sys/syspassword@hostname/servicename as sysdba @setup_samples - *---------------------------------------------------------------------------*/ - -whenever sqlerror exit failure - --- get parameters -set echo off termout on feedback off verify off -accept main_user char default pythondemo - - prompt "Name of main schema [pythondemo]: " -accept main_password char prompt "Password for &main_user: " HIDE -accept edition_user char default pythoneditions - - prompt "Name of edition schema [pythoneditions]: " -accept edition_password char prompt "Password for &edition_user: " HIDE -accept edition_name char default python_e1 - - prompt "Name of edition [python_e1]: " -set feedback on - --- perform work -@@drop_samples_exec.sql -@@setup_samples_exec.sql - -exit diff --git a/samples/sql/setup_samples_exec.sql b/samples/sql/setup_samples_exec.sql deleted file mode 100644 index bb562775..00000000 --- a/samples/sql/setup_samples_exec.sql +++ /dev/null @@ -1,558 +0,0 @@ -/*----------------------------------------------------------------------------- - * Copyright 2017, 2020, Oracle and/or its affiliates. All rights reserved. - *---------------------------------------------------------------------------*/ - -/*----------------------------------------------------------------------------- - * setup_samples_exec.sql - * This script performs the actual work of creating and populating the - * schemas with the database objects used by the sample scripts. An edition - * is also created for the demonstration of PL/SQL editioning. It is called by - * the setup_samples.sql file after acquiring the necessary parameters and also - * by the Python script setup_samples.py. - *---------------------------------------------------------------------------*/ - -alter session set nls_date_format = 'YYYY-MM-DD HH24:MI:SS' -/ -alter session set nls_numeric_characters='.,' -/ - -create user &main_user identified by &main_password -/ - -grant - create session, - create table, - create procedure, - create type, - select any dictionary, - change notification, - unlimited tablespace -to &main_user -/ - -grant aq_administrator_role to &main_user -/ - -begin - execute immediate 'begin dbms_session.sleep(0); end;'; -exception -when others then - begin - execute immediate 'grant execute on dbms_lock to &main_user'; - exception - when others then - raise_application_error(-20000, - 'Ensure the following grant is made: ' || - 'grant execute on dbms_lock to ' || user || - ' with grant option'); - end; -end; -/ - -begin - - for r in - ( select role - from dba_roles - where role in ('SODA_APP') - ) loop - execute immediate 'grant ' || r.role || ' to &main_user'; - end loop; - -end; -/ - -create user &edition_user identified by &edition_password -/ - -grant - create session, - create procedure -to &edition_user -/ - -alter user &edition_user enable editions -/ - -create edition &edition_name -/ - -grant use on edition &edition_name to &edition_user -/ - --- create types - -create type &main_user..udt_SubObject as object ( - SubNumberValue number, - SubStringValue varchar2(60) -); -/ - -create or replace type &main_user..udt_Building as object ( - BuildingId number(9), - NumFloors number(3), - Description varchar2(60), - DateBuilt date -); -/ - -create or replace type &main_user..udt_Book as object ( - Title varchar2(100), - Authors varchar2(100), - Price number(5,2) -); -/ - --- create tables - -create table &main_user..TestNumbers ( - IntCol number(9) not null, - NumberCol number(9, 2) not null, - FloatCol float not null, - UnconstrainedCol number not null, - NullableCol number(38) -) -/ - -create table &main_user..TestStrings ( - IntCol number(9) not null, - StringCol varchar2(20) not null, - RawCol raw(30) not null, - FixedCharCol char(40) not null, - NullableCol varchar2(50) -) -/ - -create table &main_user..TestCLOBs ( - IntCol number(9) not null, - CLOBCol clob not null -) -/ - -create table &main_user..TestBLOBs ( - IntCol number(9) not null, - BLOBCol blob not null -) -/ - -create table &main_user..TestTempTable ( - IntCol number(9) not null, - StringCol varchar2(400), - constraint TestTempTable_pk primary key (IntCol) -) -/ - -create table &main_user..TestUniversalRowids ( - IntCol number(9) not null, - StringCol varchar2(250) not null, - DateCol date not null, - constraint TestUniversalRowids_pk primary key (IntCol, StringCol, DateCol) -) organization index -/ - -create table &main_user..TestBuildings ( - BuildingId number(9) not null, - BuildingObj &main_user..udt_Building not null -) -/ - -create table &main_user..BigTab ( - mycol varchar2(20) -) -/ - -create table &main_user..SampleQueryTab ( - id number not null, - name varchar2(20) not null -) -/ - -create table &main_user..MyTab ( - id number, - data varchar2(20) -) -/ - -create table &main_user..ParentTable ( - ParentId number(9) not null, - Description varchar2(60) not null, - constraint ParentTable_pk primary key (ParentId) -) -/ - -create table &main_user..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 &main_user..ParentTable -) -/ - -create table &main_user..Ptab ( - myid number, - mydata varchar(20) -) -/ - -create table &main_user..PlsqlSessionCallbacks ( - RequestedTag varchar2(250), - ActualTag varchar2(250), - FixupTimestamp timestamp -) -/ - --- create queue table, queues and subscribers for demonstrating Advanced Queuing -begin - - dbms_aqadm.create_queue_table('&main_user..BOOK_QUEUE_TAB', - '&main_user..UDT_BOOK'); - dbms_aqadm.create_queue('&main_user..DEMO_BOOK_QUEUE', - '&main_user..BOOK_QUEUE_TAB'); - dbms_aqadm.start_queue('&main_user..DEMO_BOOK_QUEUE'); - - dbms_aqadm.create_queue_table('&main_user..RAW_QUEUE_TAB', 'RAW'); - dbms_aqadm.create_queue('&main_user..DEMO_RAW_QUEUE', - '&main_user..RAW_QUEUE_TAB'); - dbms_aqadm.start_queue('&main_user..DEMO_RAW_QUEUE'); - - dbms_aqadm.create_queue_table('&main_user..RAW_QUEUE_MULTI_TAB', 'RAW', - multiple_consumers => true); - dbms_aqadm.create_queue('&main_user..DEMO_RAW_QUEUE_MULTI', - '&main_user..RAW_QUEUE_MULTI_TAB'); - dbms_aqadm.start_queue('&main_user..DEMO_RAW_QUEUE_MULTI'); - - dbms_aqadm.add_subscriber('&main_user..DEMO_RAW_QUEUE_MULTI', - sys.aq$_agent('SUBSCRIBER_A', null, null)); - dbms_aqadm.add_subscriber('&main_user..DEMO_RAW_QUEUE_MULTI', - sys.aq$_agent('SUBSCRIBER_B', null, null)); - -end; -/ - --- populate tables - -begin - for i in 1..20000 loop - insert into &main_user..BigTab (mycol) - values (dbms_random.string('A', 20)); - end loop; -end; -/ - -begin - for i in 1..10 loop - insert into &main_user..TestNumbers - values (i, i + i * 0.25, i + i * .75, i * i * i + i *.5, - decode(mod(i, 2), 0, null, power(143, i))); - end loop; -end; -/ - -declare - - t_RawValue raw(30); - - function ConvertHexDigit(a_Value number) return varchar2 is - begin - if a_Value between 0 and 9 then - return to_char(a_Value); - end if; - return chr(ascii('A') + a_Value - 10); - end; - - function ConvertToHex(a_Value varchar2) return varchar2 is - t_HexValue varchar2(60); - t_Digit number; - begin - for i in 1..length(a_Value) loop - t_Digit := ascii(substr(a_Value, i, 1)); - t_HexValue := t_HexValue || - ConvertHexDigit(trunc(t_Digit / 16)) || - ConvertHexDigit(mod(t_Digit, 16)); - end loop; - return t_HexValue; - end; - -begin - for i in 1..10 loop - t_RawValue := hextoraw(ConvertToHex('Raw ' || to_char(i))); - insert into &main_user..TestStrings - values (i, 'String ' || to_char(i), t_RawValue, - 'Fixed Char ' || to_char(i), - decode(mod(i, 2), 0, null, 'Nullable ' || to_char(i))); - end loop; -end; -/ - -insert into &main_user..ParentTable values (10, 'Parent 10') -/ -insert into &main_user..ParentTable values (20, 'Parent 20') -/ -insert into &main_user..ParentTable values (30, 'Parent 30') -/ -insert into &main_user..ParentTable values (40, 'Parent 40') -/ -insert into &main_user..ParentTable values (50, 'Parent 50') -/ - -insert into &main_user..ChildTable values (1001, 10, 'Child A of Parent 10') -/ -insert into &main_user..ChildTable values (1002, 20, 'Child A of Parent 20') -/ -insert into &main_user..ChildTable values (1003, 20, 'Child B of Parent 20') -/ -insert into &main_user..ChildTable values (1004, 20, 'Child C of Parent 20') -/ -insert into &main_user..ChildTable values (1005, 30, 'Child A of Parent 30') -/ -insert into &main_user..ChildTable values (1006, 30, 'Child B of Parent 30') -/ -insert into &main_user..ChildTable values (1007, 40, 'Child A of Parent 40') -/ -insert into &main_user..ChildTable values (1008, 40, 'Child B of Parent 40') -/ -insert into &main_user..ChildTable values (1009, 40, 'Child C of Parent 40') -/ -insert into &main_user..ChildTable values (1010, 40, 'Child D of Parent 40') -/ -insert into &main_user..ChildTable values (1011, 40, 'Child E of Parent 40') -/ -insert into &main_user..ChildTable values (1012, 50, 'Child A of Parent 50') -/ -insert into &main_user..ChildTable values (1013, 50, 'Child B of Parent 50') -/ -insert into &main_user..ChildTable values (1014, 50, 'Child C of Parent 50') -/ -insert into &main_user..ChildTable values (1015, 50, 'Child D of Parent 50') -/ - -insert into &main_user..SampleQueryTab values (1, 'Anthony') -/ -insert into &main_user..SampleQueryTab values (2, 'Barbie') -/ -insert into &main_user..SampleQueryTab values (3, 'Chris') -/ -insert into &main_user..SampleQueryTab values (4, 'Dazza') -/ -insert into &main_user..SampleQueryTab values (5, 'Erin') -/ -insert into &main_user..SampleQueryTab values (6, 'Frankie') -/ -insert into &main_user..SampleQueryTab values (7, 'Gerri') -/ - -commit -/ - --- --- For PL/SQL Examples --- - -create or replace function &main_user..myfunc ( - a_Data varchar2, - a_Id number -) return number as -begin - insert into &main_user..ptab (mydata, myid) values (a_Data, a_Id); - return (a_Id * 2); -end; -/ - -create or replace procedure &main_user..myproc ( - a_Value1 number, - a_Value2 out number -) as -begin - a_Value2 := a_Value1 * 2; -end; -/ - -create or replace procedure &main_user..myrefcursorproc ( - a_StartingValue number, - a_EndingValue number, - a_RefCursor out sys_refcursor -) as -begin - open a_RefCursor for - select * - from TestStrings - where IntCol between a_StartingValue and a_EndingValue; -end; -/ - -create procedure &main_user..myrefcursorproc2 ( - a_RefCursor out sys_refcursor -) as -begin - open a_RefCursor for - select * - from TestTempTable; -end; -/ - --- --- Create package for demoing PL/SQL collections and records. --- - -create or replace package &main_user..pkg_Demo as - - type udt_StringList is table of varchar2(100) index by binary_integer; - - type udt_DemoRecord is record ( - NumberValue number, - StringValue varchar2(30), - DateValue date, - BooleanValue boolean - ); - - procedure DemoCollectionOut ( - a_Value out nocopy udt_StringList - ); - - procedure DemoRecordsInOut ( - a_Value in out nocopy udt_DemoRecord - ); - -end; -/ - -create or replace package body &main_user..pkg_Demo 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; - - 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; -/ - --- --- Create package for demoing PL/SQL session callback --- - -create or replace package &main_user..pkg_SessionCallback as - - procedure TheCallback ( - a_RequestedTag varchar2, - a_ActualTag varchar2 - ); - -end; -/ - -create or replace package body &main_user..pkg_SessionCallback as - - type udt_Properties is table of varchar2(64) index by varchar2(64); - - procedure LogCall ( - a_RequestedTag varchar2, - a_ActualTag varchar2 - ) is - pragma autonomous_transaction; - begin - insert into PlsqlSessionCallbacks - values (a_RequestedTag, a_ActualTag, systimestamp); - commit; - end; - - procedure ParseProperty ( - a_Property varchar2, - a_Name out nocopy varchar2, - a_Value out nocopy varchar2 - ) is - t_Pos number; - begin - t_Pos := instr(a_Property, '='); - if t_Pos = 0 then - raise_application_error(-20000, 'Tag must contain key=value pairs'); - end if; - a_Name := substr(a_Property, 1, t_Pos - 1); - a_Value := substr(a_Property, t_Pos + 1); - end; - - procedure SetProperty ( - a_Name varchar2, - a_Value varchar2 - ) is - t_ValidValues udt_Properties; - begin - if a_Name = 'TIME_ZONE' then - t_ValidValues('UTC') := 'UTC'; - t_ValidValues('MST') := '-07:00'; - elsif a_Name = 'NLS_DATE_FORMAT' then - t_ValidValues('SIMPLE') := 'YYYY-MM-DD HH24:MI'; - t_ValidValues('FULL') := 'YYYY-MM-DD HH24:MI:SS'; - else - raise_application_error(-20000, 'Unsupported session setting'); - end if; - if not t_ValidValues.exists(a_Value) then - raise_application_error(-20000, 'Unsupported session setting'); - end if; - execute immediate - 'ALTER SESSION SET ' || a_Name || '=''' || - t_ValidValues(a_Value) || ''''; - end; - - procedure ParseTag ( - a_Tag varchar2, - a_Properties out nocopy udt_Properties - ) is - t_PropertyName varchar2(64); - t_PropertyValue varchar2(64); - t_StartPos number; - t_EndPos number; - begin - t_StartPos := 1; - while t_StartPos < length(a_Tag) loop - t_EndPos := instr(a_Tag, ';', t_StartPos); - if t_EndPos = 0 then - t_EndPos := length(a_Tag) + 1; - end if; - ParseProperty(substr(a_Tag, t_StartPos, t_EndPos - t_StartPos), - t_PropertyName, t_PropertyValue); - a_Properties(t_PropertyName) := t_PropertyValue; - t_StartPos := t_EndPos + 1; - end loop; - end; - - procedure TheCallback ( - a_RequestedTag varchar2, - a_ActualTag varchar2 - ) is - t_RequestedProps udt_Properties; - t_ActualProps udt_Properties; - t_PropertyName varchar2(64); - begin - LogCall(a_RequestedTag, a_ActualTag); - ParseTag(a_RequestedTag, t_RequestedProps); - ParseTag(a_ActualTag, t_ActualProps); - t_PropertyName := t_RequestedProps.first; - while t_PropertyName is not null loop - if not t_ActualProps.exists(t_PropertyName) or - t_ActualProps(t_PropertyName) != - t_RequestedProps(t_PropertyName) then - SetProperty(t_PropertyName, t_RequestedProps(t_PropertyName)); - end if; - t_PropertyName := t_RequestedProps.next(t_PropertyName); - end loop; - end; - -end; -/ diff --git a/samples/subclassing.py b/samples/subclassing.py deleted file mode 100644 index 150f0f75..00000000 --- a/samples/subclassing.py +++ /dev/null @@ -1,53 +0,0 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2016, 2021, Oracle and/or its affiliates. All rights reserved. -#------------------------------------------------------------------------------ - -#------------------------------------------------------------------------------ -# subclassing.py -# -# Demonstrate how to subclass connections and cursors in order to add -# additional functionality (like logging) or create specialized interfaces for -# paticular applications. -#------------------------------------------------------------------------------ - -import cx_Oracle as oracledb -import sample_env - -# sample subclassed connection which overrides the constructor (so no -# parameters are required) and the cursor() method (so that the subclassed -# cursor is returned instead of the default cursor implementation) -class Connection(oracledb.Connection): - - def __init__(self): - connect_string = sample_env.get_main_connect_string() - print("CONNECT to database") - super().__init__(connect_string) - - def cursor(self): - return Cursor(self) - - -# sample subclassed cursor which overrides the execute() and fetchone() -# methods in order to perform some simple logging -class Cursor(oracledb.Cursor): - - def execute(self, statement, args): - print("EXECUTE", statement) - print("ARGS:") - for arg_index, arg in enumerate(args): - print(" ", arg_index + 1, "=>", repr(arg)) - return super().execute(statement, args) - - def fetchone(self): - print("FETCH ONE") - return super().fetchone() - - -# create instances of the subclassed connection and cursor -connection = Connection() -cursor = connection.cursor() - -# demonstrate that the subclassed connection and cursor are being used -cursor.execute("select count(*) from ChildTable where ParentId = :1", (30,)) -count, = cursor.fetchone() -print("COUNT:", int(count)) diff --git a/samples/transaction_guard.py b/samples/transaction_guard.py deleted file mode 100644 index 82284c01..00000000 --- a/samples/transaction_guard.py +++ /dev/null @@ -1,77 +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. -#------------------------------------------------------------------------------ - -#------------------------------------------------------------------------------ -# transaction_guard.py -# This script demonstrates the use of Transaction Guard to verify if a -# transaction has completed, ensuring that a duplicate transaction is not -# created or attempted if the application chooses to handle the error. This -# feature is only available in Oracle Database 12.1. It follows loosely the -# OCI sample provided by Oracle in its documentation about OCI and Transaction -# Guard. -# -# Run the following as SYSDBA to set up Transaction Guard -# -# grant execute on dbms_app_cont to pythondemo; -# -# declare -# t_Params dbms_service.svc_parameter_array; -# begin -# t_Params('COMMIT_OUTCOME') := 'true'; -# t_Params('RETENTION_TIMEOUT') := 604800; -# dbms_service.create_service('orcl-tg', 'orcl-tg', t_Params); -# dbms_service.start_service('orcl-tg'); -# end; -# / -# -# This script requires cx_Oracle 5.3 and higher. -#------------------------------------------------------------------------------ - -import sys - -import cx_Oracle as oracledb -import sample_env - -# constants -CONNECT_STRING = "localhost/orcl-tg" - -# create transaction and generate a recoverable error -pool = oracledb.SessionPool(user=sample_env.get_main_user(), - password=sample_env.get_main_password(), - dsn=CONNECT_STRING, min=1, max=9, increment=2) -connection = pool.acquire() -cursor = connection.cursor() -cursor.execute(""" - delete from TestTempTable - where IntCol = 1""") -cursor.execute(""" - insert into TestTempTable - values (1, null)""") -input("Please kill %s session now. Press ENTER when complete." % \ - sample_env.get_main_user()) -try: - connection.commit() # this should fail - sys.exit("Session was not killed. Terminating.") -except oracledb.DatabaseError as e: - error_obj, = e.args - if not error_obj.isrecoverable: - sys.exit("Session is not recoverable. Terminating.") -ltxid = connection.ltxid -if not ltxid: - sys.exit("Logical transaction not available. Terminating.") -pool.drop(connection) - -# check if previous transaction completed -connection = pool.acquire() -cursor = connection.cursor() -args = (oracledb.Binary(ltxid), cursor.var(bool), cursor.var(bool)) -_, committed, completed = cursor.callproc("dbms_app_cont.get_ltxid_outcome", - args) -print("Failed transaction was committed:", committed) -print("Failed call was completed:", completed) diff --git a/samples/tutorial/Python-and-Oracle-Database-Scripting-for-the-Future.html b/samples/tutorial/Python-and-Oracle-Database-Scripting-for-the-Future.html deleted file mode 100644 index ded77874..00000000 --- a/samples/tutorial/Python-and-Oracle-Database-Scripting-for-the-Future.html +++ /dev/null @@ -1,2682 +0,0 @@ - - - -Python and Oracle Database Tutorial: Scripting for the Future - - - - - - - -

Python and Oracle Database Tutorial: Scripting for the Future

- - Python cx_Oracle logo - -

Contents

- -
    -
  • Overview
  • -
  • Setup
  • -
  • Connecting to Oracle -
      -
    • 1.1 Creating a basic connection
    • -
    • 1.2 Indentation indicates code structure
    • -
    • 1.3 Executing a query
    • -
    • 1.4 Closing connections
    • -
    • 1.5 Checking versions
    • -
    -
  • -
  • Connection Pooling -
      -
    • 2.1 Connection pooling
    • -
    • 2.2 Connection pool experiments
    • -
    • 2.3 Creating a DRCP Connection
    • -
    • 2.4 Connection pooling and DRCP
    • -
    • 2.5 More DRCP investigation
    • -
    -
  • -
  • Fetching Data -
      -
    • 3.1 A simple query
    • -
    • 3.2 Using fetchone()
    • -
    • 3.3 Using fetchmany()
    • -
    • 3.4 Scrollable cursors
    • -
    • 3.5 Tuning with arraysize and prefetchrows
    • -
    -
  • -
  • Binding Data -
      -
    • 4.1 Binding in queries
    • -
    • 4.2 Binding in inserts
    • -
    • 4.3 Batcherrors
    • -
    • 4.4 Binding named objects
    • -
    -
  • -
  • PL/SQL -
      -
    • 5.1 PL/SQL functions
    • -
    • 5.2 PL/SQL procedures
    • -
    -
  • -
  • Type Handlers -
      -
    • 6.1 Basic output type handler
    • -
    • 6.2 Output type handlers and variable converters
    • -
    • 6.3 Input type handlers
    • -
    -
  • -
  • LOBs -
      -
    • 7.1 Fetching a CLOB using a locator
    • -
    • 7.2 Fetching a CLOB as a string
    • -
    -
  • -
  • Rowfactory functions -
      -
    • 8.1 Rowfactory for mapping column names
    • -
    -
  • -
  • Subclassing connections and cursors -
      -
    • 9.1 Subclassing connections
    • -
    • 9.2 Subclassing cursors
    • -
    -
  • -
  • Advanced Queuing -
      -
    • 10.1 Message passing with Oracle Advanced Queuing
    • -
    -
  • -
  • Simple Oracle Document Access (SODA) -
      -
    • 11.1 Inserting JSON Documents
    • -
    • 11.2 Searching SODA Documents
    • -
    -
  • -
  • Summary
  • -
  • Appendix: Python Primer
  • -
  • Resources
  • -
- - -

Overview

- -

This tutorial is an introduction to using Python with Oracle Database. It - contains beginner and advanced material. Sections can be done in any order. - Choose the content that interests you and your skill level. The tutorial has - scripts to run and modify, and has suggested solutions.

- -

Python is a popular general purpose dynamic scripting language. The - cx_Oracle interface provides the Python API to access Oracle Database.

- -

If you are new to Python review the Appendix: - Python Primer to gain an understanding of the language.

- -

When you have finished this tutorial, we recommend reviewing the cx_Oracle - documention.

- -

The original copy of these instructions that you are reading is here.

- -

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. If the database is local, - the client libraries from the Oracle Database software installation can be - used.

- - Python cx_Oracle architecture - - -

Setup

- -
    - -
  • -

    Install software

    - -

    To get going, follow either of the quick start instructions:

    - - - - -

    For this tutorial, you will need Python 3.6 (or later), cx_Oracle 7.3 (or later), and access to Oracle Database.

    - -

    The Advanced Queuing section requires Python cx_Oracle to be using - Oracle client libraries 12.2 or later. The SODA section requires Oracle - Database 18 or later, and Python cx_Oracle must be using Oracle libraries - from 18.5, or later.

    - -
  • - - -
  • -

    Download the tutorial scripts

    - -

    The Python scripts used in this example are in the cx_Oracle GitHub repository.

    - -

    Download a zip file of the repository from here and unzip it. Alternatively you can use 'git' to clone the repository with git clone https://github.com/oracle/python-cx_Oracle.git

    - -

    The samples/tutorial directory has scripts to run and - modify. The samples/tutorial/solutions directory has scripts - with suggested code changes.

    - -
  • - -
  • Create a database user

    - -

    If you have an existing user, you may be able to use it for most - examples (some examples may require extra permissions).

    - -

    If you need to create a new user, review the grants created in - samples/tutorial/sql/create_user.sql. Then open a terminal - window, change to the samples/tutorial/sql directory, and - run the create_user.sql script as the SYSTEM user, for - example:

    - -
    -cd samples/tutorial/sql
    -sqlplus -l system/systempassword@localhost/orclpdb1 @create_user
    -
    - -

    The example above connects as the SYSTEM user. The connection - string is "localhost/orclpdb1", meaning use the database service - "orclpdb1" running on localhost (the computer you are running SQL*Plus - on). Substitute values for your environment. If you are using Oracle - Autonomous Database, use the ADMIN user instead of SYSTEM.

    - -

    When the tutorial is finished, the drop_user.sql script - in the same directory can be used to remove the tutorial user.

    - -
  • - -
  • Install the sample tables

    - -

    Once you have a database user, then you can create the tutorial - tables by running a command like this, using your values for the - tutorial username, password and connection string:

    - -
    -sqlplus -l pythonhol/welcome@localhost/orclpdb1 @setup_tables
    -
    - -
  • - -
  • Start the Database Resident Connection Pool (DRCP)

    - -

    If you want to try the DRCP examples in section 2, start the DRCP - pool. (The pool is already started in Oracle Autonomous Database).

    - -

    Run SQL*Plus with SYSDBA privileges, for example:

    - -
    -sqlplus -l sys/syspassword@localhost/orclcdb as sysdba
    -
    - -

    and execute the command:

    - -
    -execute dbms_connection_pool.start_pool()
    -
    - -

    Note you may need to do this in the container database, not a pluggable database.

    - -
  • -
  • -

    Review the connection credentials used by the tutorial scripts

    - -

    Review db_config.py and db_config.sql in - the tutorial directory. These are included in other - Python and SQL files.

    - -

    Edit db_config.py and change the default values to - match the connection information for your environment. Alternatively - you can set the given envionment variables in your terminal - window. For example, the default username is "pythonhol" unless the - envionment variable "PYTHON_USER" contains a different username. The - default connection string is for the 'orclpdb1' database service on - the same machine as Python. (In Python Database API terminology, the - connection string parameter is called the "data source name", or - "dsn".) Using envionment variables is convenient because you will not - be asked to re-enter the password when you run scripts:

    - -
    -user = os.environ.get("PYTHON_USER", "pythonhol")
    -
    -dsn = os.environ.get("PYTHON_CONNECT_STRING", "localhost/orclpdb1")
    -
    -pw = os.environ.get("PYTHON_PASSWORD")
    -if pw is None:
    -    pw = getpass.getpass("Enter password for %s: " % user)
    -
    - -

    Also change the default username and connection string in the SQL*Plus -configuration file db_config.sql:

    - -
    --- Default database username
    -def user = "pythonhol"
    -
    --- Default database connection string
    -def connect_string = "localhost/orclpdb1"
    -
    --- Prompt for the password
    -accept pw char prompt 'Enter database password for &user: ' hide
    -
    - - -

    The tutorial instructions may need adjusting, depending on how you - have set up your environment.

    -
  • - -
  • -

    Review the Instant Client library path

    - -

    Review the Oracle Client library path settings in - db_config.py. If cx_Oracle cannot locate Oracle Client - libraries, then your applications will fail with an error like - "DPI-1047: Cannot locate a 64-bit Oracle Client library".

    - -
    -# On Linux this must be None.
    -# Instead, the Oracle environment must be set before Python starts.
    -instant_client_dir = None
    -
    -# On Windows, if your database is on the same machine, comment these lines out
    -# and let instant_client_dir be None.  Otherwise, set this to your Instant
    -# Client directory.  Note the use of the raw string r"..."  so backslashes can
    -# be used as directory separators.
    -if sys.platform.startswith("win"):
    -    instant_client_dir = r"c:\oracle\instantclient_19_10"
    -
    -# On macOS (Intel x86) set the directory to your Instant Client directory
    -if sys.platform.startswith("darwin"):
    -    instant_client_dir = os.environ.get("HOME")+"/Downloads/instantclient_19_8"
    -
    -# This can be called at most once per process.
    -if instant_client_dir is not None:
    -    cx_Oracle.init_oracle_client(lib_dir=instant_client_dir)
    -
    - -

    Set instant_client_dir to None or to a - valid path according to the following notes:

    - -
      -
    • - -

      If you are on macOS or Windows, and you have installed Oracle - Instant Client libraries because your database is on a remote - machine, then set instant_client_dir to the path of - the Instant Client libraries.

      - -
    • - -
    • - -

      If you are on Windows and have a local database installed, then - comment out the two Windows lines, so that - instant_client_dir remains None.

      - -
    • - -
    • - -

      In all other cases (including Linux with Oracle Instant - Client), make sure that instant_client_dir is set to - None. In these cases you must make sure that the - Oracle libraries from Instant Client or your ORACLE_HOME are in - your system library search path before you start Python. On Linux, - the path can be configured with ldconfig or with the - LD_LIBRARY_PATH environment variables.

      - -
    • - -
    - -
  • -
- -

1. Connecting to Oracle

- -

You can connect from Python to a local, remote or cloud database. Documentation -link for further reading: Connecting to Oracle Database.

- -
    -
  • -

    1.1 Creating a basic connection

    -

    Review the code contained in connect.py:

    -
    -import cx_Oracle
    -import db_config
    -
    -con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn)
    -print("Database version:", con.version)
    -
    - -

    The cx_Oracle module is imported to provide the API for accessing - the Oracle database. Many inbuilt and third party modules can be - included in Python scripts this way.

    - -

    The connect() method is passed the username, the - password and the connection string that you configured in the - db_config.py module. In this case, Oracle's Easy Connect connection - string syntax is used. It consists of the hostname of your machine, - localhost, and the database service name - orclpdb1. (In Python Database API terminology, the - connection string parameter is called the "data source name", or - "dsn".)

    - -

    Open a command terminal and change to the tutorial directory:

    - -
    cd samples/tutorial
    - -

    Run the Python script:

    - -
    python connect.py
    - -

    The version number of the database should be displayed. An - exception is raised if the connection fails. Adjust the username, - password or connection string parameters to invalid values to see the - exception.

    - -

    cx_Oracle also supports "external authentication", which allows - connections without needing usernames and passwords to be embedded in - the code. Authentication would then instead be performed by, for - example, LDAP or Oracle Wallets.

    - -
  • - -
  • -

    1.2 Indentation indicates code structure

    - -

    There are no statement terminators or begin/end keywords - or braces to indicate blocks of code.

    - -

    Open connect.py in an editor. Indent the - print statement with some spaces:

    - -
    -import cx_Oracle
    -import db_config
    -
    -con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn)
    -  print("Database version:", con.version)
    -
    - -

    Save the script and run it again:

    - -
    python connect.py 
    - -

    This raises an exception about the indentation. The - number of spaces or tabs must be consistent in each block; - otherwise, the Python interpreter will either raise an - exception or execute code unexpectedly.

    - -

    Python may not always be able to identify accidental - from deliberate indentation. Check your indentation is - correct before running each example. Make sure to indent - all statement blocks equally. Note the sample files - use spaces, not tabs.

    - -
  • - -
  • -

    1.3 Executing a query

    - -

    Open query.py in an editor. It looks like:

    - -
    -import cx_Oracle
    -import db_config
    -
    -con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn)
    -
    - -

    Edit the file and add the code shown in bold below:

    - -
    -import cx_Oracle
    -import db_config
    -
    -con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn)
    -
    -cur = con.cursor()
    -cur.execute("select * from dept order by deptno")
    -res = cur.fetchall()
    -for row in res:
    -    print(row)
    -
    - -

    Make sure the print(row) line is indented. This lab uses spaces, not tabs.

    - -

    The code executes a query and fetches all data.

    - -

    Save the file and run it:

    - -
    python query.py
    - -

    In each loop iteration a new row is stored in - row as a Python "tuple" and is displayed.

    - -

    Fetching data is described further in section 3.

    -
  • - -
  • -

    1.4 Closing connections

    - -

    Connections and other resources used by cx_Oracle will - automatically be closed at the end of scope. This is a - common programming style that takes care of the correct - order of resource closure.

    - -

    Resources can also be explicitly closed to free up database - resources if they are no longer needed. This is strongly recommended - in blocks of code that remain active for some time.

    - -

    Open query.py in an editor and add calls to - close the cursor and connection like:

    - -
    -import cx_Oracle
    -import db_config
    -
    -con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn)
    -
    -cur = con.cursor()
    -cur.execute("select * from dept order by deptno")
    -res = cur.fetchall()
    -for row in res:
    -    print(row)
    -
    -cur.close()
    -con.close()
    -
    - -

    Running the script completes without error:

    - -
    python query.py
    - -

    If you swap the order of the two close() calls you will see an error.

    -
  • - -
  • -

    1.5 Checking versions

    - -

    Review the code contained in versions.py:

    - -
    -import cx_Oracle
    -import db_config
    -
    -con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn)
    -
    -print(cx_Oracle.version)
    - -

    Run the script:

    - -
    python versions.py
    - -

    This gives the version of the cx_Oracle interface.

    - -

    Edit the file to print the version of the database, and of the Oracle client libraries used by cx_Oracle:

    - -
    -import cx_Oracle
    -import db_config
    -
    -con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn)
    -
    -print(cx_Oracle.version)
    -print("Database version:", con.version)
    -print("Client version:", cx_Oracle.clientversion())
    -
    - -

    When the script is run, it will display:

    - -
    -7.1.0
    -Database version: 19.3.0.0.0
    -Client version: (19, 8, 0, 0, 0)
    -
    - -

    Note the client version is a tuple.

    - -

    Any cx_Oracle installation can connect to older and newer - Oracle Database versions. By checking the Oracle Database - and client versions numbers, the application can make use of - the best Oracle features available.

    - -
  • - -
- -

2. Connection Pooling

- -

Connection pooling is important for performance in when multi-threaded - applications frequently connect and disconnect from the database. Pooling - also gives the best support for Oracle high availability features. - Documentation link for further reading: Connection Pooling.

- -
    -
  • 2.1 Connection pooling

    - -

    Review the code contained in connect_pool.py:

    -
    -import cx_Oracle
    -import threading
    -import db_config
    -
    -pool = cx_Oracle.SessionPool(db_config.user, db_config.pw, db_config.dsn,
    -                             min = 2, max = 5, increment = 1, threaded = True,
    -                             getmode = cx_Oracle.SPOOL_ATTRVAL_WAIT)
    -
    -def Query():
    -    con = pool.acquire()
    -    cur = con.cursor()
    -    for i in range(4):
    -        cur.execute("select myseq.nextval from dual")
    -        seqval, = cur.fetchone()
    -        print("Thread", threading.current_thread().name, "fetched sequence =", seqval)
    -
    -thread1 = threading.Thread(name='#1', target=Query)
    -thread1.start()
    -
    -thread2 = threading.Thread(name='#2', target=Query)
    -thread2.start()
    -
    -thread1.join()
    -thread2.join()
    -
    -print("All done!")
    -
    - -

    The SessionPool() function creates a pool of - Oracle connections for the user. Connections in the pool can - be used by cx_Oracle by calling pool.acquire(). - The initial pool size is 2 connections. The maximum size is 5 - connections. When the pool needs to grow, then 1 new connection - will be created at a time. The pool can shrink back to the - minimum size of 2 when connections are no longer in use.

    - -

    The def Query(): line creates a method that - is called by each thread.

    - -

    In the method, the pool.acquire() call gets - one connection from the pool (as long as less than 5 are - already in use). This connection is used in a loop of 4 - iterations to query the sequence myseq. At the - end of the method, cx_Oracle will automatically close the - cursor and release the connection back to the pool for - reuse.

    - -

    The seqval, = cur.fetchone() line fetches a - row and puts the single value contained in the result tuple - into the variable seqval. Without the comma, - the value in seqval would be a tuple like - "(1,)".

    - -

    Two threads are created, each invoking the - Query() method.

    - -

    In a command terminal, run:

    - -
    python connect_pool.py
    - -

    The output shows interleaved query results as each thread fetches -values independently. The order of interleaving may vary from run to -run.

    - -
  • - -
  • -

    2.2 Connection pool experiments

    - - -

    Review connect_pool2.py, which has a loop for the number -of threads, each iteration invoking the Query() method:

    - -
    -import cx_Oracle
    -import threading
    -import db_config
    -
    -pool = cx_Oracle.SessionPool(db_config.user, db_config.pw, db_config.dsn,
    -                             min = 2, max = 5, increment = 1, threaded = True,
    -                             getmode = cx_Oracle.SPOOL_ATTRVAL_WAIT)
    -
    -def Query():
    -    con = pool.acquire()
    -    cur = con.cursor()
    -    for i in range(4):
    -        cur.execute("select myseq.nextval from dual")
    -        seqval, = cur.fetchone()
    -        print("Thread", threading.current_thread().name, "fetched sequence =", seqval)
    -
    -numberOfThreads = 2
    -threadArray = []
    -
    -for i in range(numberOfThreads):
    -    thread = threading.Thread(name = '#' + str(i), target = Query)
    -    threadArray.append(thread)
    -    thread.start()
    -
    -for t in threadArray:
    -    t.join()
    -
    -print("All done!")
    -
    - -

    In a command terminal, run:

    - -
    python connect_pool2.py
    - -

    Experiment with different values of the pool parameters and -numberOfThreads. Larger initial pool sizes will make the pool -creation slower, but the connections will be available immediately when needed. -

    - -

    Try changing getmode to -cx_Oracle.SPOOL_ATTRVAL_NOWAIT. When numberOfThreads -exceeds the maximum size of the pool, the acquire() call will now -generate an error such as "ORA-24459: OCISessionGet() timed out waiting for pool -to create new connections".

    - -

    Pool configurations where min is the same as -max (and increment = 0) are often -recommended as a best practice. This avoids connection storms on the -database server.

    - -
  • - -
  • -

    2.3 Creating a DRCP Connection

    - -

    Database Resident Connection Pooling allows multiple Python - processes on multiple machines to share a small pool of database - server processes.

    - -

    Below left is a diagram without DRCP. Every application standalone - connection (or cx_Oracle connection-pool connection) has its own database - server process. Standalone application connect() and close calls - require the expensive create and destroy of those database server processes. - cx_Oracle connection pools reduce these costs by keeping database server - processes open, but every cx_Oracle connection pool will requires its own set - of database server processes, even if they are not doing database work: these - idle server processes consumes database host resources. Below right is a - diagram with DRCP. Scripts and Python processes can share database servers - from a precreated pool of servers and return them when they are not in use. -

    - - - - - - -
    - Picture of 3-tier application architecture without DRCP showing connections from multiple application processes each going to a server process in the database tier -

    Without DRCP

    -
    - Picture of 3-tier application architecture with DRCP showing connections from multiple application processes going to a pool of server processes in the database tier -

    With DRCP

    -
    - -

    DRCP is useful when the database host machine does not have enough memory - to handle the number of database server processes required. If DRCP is - enabled, it is best used in conjunction with cx_Oracle's connection pooling. - However, if the database host memory is large enough, then the default, - 'dedicated' server process model is generally recommended. This can be with - or without a cx_Oracle connection pool, depending on the connection rate.

    - -

    Batch scripts doing long running jobs should generally use - dedicated connections. Both dedicated and DRCP servers can be used - together in the same application or database.

    - -

    Review the code contained in connect_drcp.py:

    - -
    -import cx_Oracle
    -import db_config
    -
    -con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn + ":pooled",
    -                        cclass="PYTHONHOL", purity=cx_Oracle.ATTR_PURITY_SELF)
    -print("Database version:", con.version)
    -
    - -

    This is similar to connect.py but - ":pooled" is appended to the connection string, telling - the database to use a pooled server. A Connection Class "PYTHONHOL" is also - passed into the connect() method to allow grouping of database - servers to applications. Note with Autonomous Database, the connection string - has a different form, see the ADB documentation.

    - -

    The "purity" of the connection is defined as the - ATTR_PURITY_SELF constant, meaning the session state - (such as the default date format) might be retained between - connection calls, giving performance benefits. Session information - will be discarded if a pooled server is later reused by an - application with a different connection class name.

    - -

    Applications that should never share session information should - use a different connection class and/or use - ATTR_PURITY_NEW to force creation of a new - session. This reduces overall scalability but prevents applications - mis-using session information.

    - -

    Run connect_drcp.py in a terminal window.

    - -
    python connect_drcp.py
    - -

    The output is simply the version of the database.

    - -
  • - -
  • -

    2.4 Connection pooling and DRCP

    - -

    DRCP works well with cx_Oracle's connection pooling.

    - -

    Edit connect_pool2.py, reset any changed pool options, and modify it to use DRCP:

    -
    -import cx_Oracle
    -import threading
    -
    -pool = cx_Oracle.SessionPool(db_config.user, db_config.pw, db_config.dsn + ":pooled",
    -                             min = 2, max = 5, increment = 1, threaded = True,
    -                             getmode = cx_Oracle.SPOOL_ATTRVAL_WAIT)
    -
    -def Query():
    -    con = pool.acquire(cclass = "PYTHONHOL", purity = cx_Oracle.ATTR_PURITY_SELF)
    -    cur = conn.cursor()
    -    for i in range(4):
    -        cur.execute("select myseq.nextval from dual")
    -        seqval, = cur.fetchone()
    -        print("Thread", threading.current_thread().name, "fetched sequence =", seqval)
    -
    -numberOfThreads = 2
    -threadArray = []
    -
    -for i in range(numberOfThreads):
    -    thread = threading.Thread(name = '#' + str(i), target = Query)
    -    threadArray.append(thread)
    -    thread.start()
    -
    -for t in threadArray:
    -    t.join()
    -
    -print("All done!")
    -
    - -

    The script logic does not need to be changed to benefit from - DRCP connection pooling.

    - -

    Run the script:

    - -
    python connect_pool2.py
    - -

    Review drcp_query.sql and set the connection string to - your database. Then open a new a terminal window and invoke SQL*Plus:

    - -
    sqlplus /nolog @drcp_query.sql
    - -

    This will prompt for the SYSTEM password and the database connection - string. With Pluggable databases, you will need to connect to the - container database. Note that with ADB, this view does not contain - rows, so running this script is not useful.

    - -

    For other databases, the script shows the number of connection requests - made to the pool since the database was started ("NUM_REQUESTS"), how many - of those reused a pooled server's session ("NUM_HITS"), and how many had - to create new sessions ("NUM_MISSES"). Typically the goal is a low number - of misses.

    - -

    To see the pool configuration you can query DBA_CPOOL_INFO.

    -
  • - -
  • -

    2.5 More DRCP investigation

    - -

    To explore the behaviors of cx_Oracle connection pooling and DRCP pooling futher, - you could try changing the purity to - cx_Oracle.ATTR_PURITY_NEW to see the effect on the - DRCP NUM_MISSES statistic.

    - -

    Another experiement is to include the time module at the file - top:

    - -
    -import time
    - -

    and add calls to time.sleep(1) in the code, for - example in the query loop. Then look at the way the threads execute. Use - drcp_query.sql to monitor the pool's behavior.

    - - -
  • -
- -

3. Fetching Data

- - -

Executing SELECT queries is the primary way to get data from Oracle - Database. Documentation link for further reading: SQL Queries.

- -
    -
  • 3.1 A simple query

    - -

    There are a number of functions you can use to query an Oracle - database, but the basics of querying are always the same:

    - -

    1. Execute the statement.
    - 2. Bind data values (optional).
    - 3. Fetch the results from the database.

    - -

    Review the code contained in query2.py:

    - -
    -import cx_Oracle
    -import db_config
    -
    -con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn)
    -
    -cur = con.cursor()
    -cur.execute("select * from dept order by deptno")
    -for deptno, dname, loc in cur:
    -    print("Department number: ", deptno)
    -    print("Department name: ", dname)
    -    print("Department location:", loc)
    -
    - -

    The cursor() method opens a cursor for statements to use.

    - -

    The execute() method parses and executes the statement.

    - -

    The loop fetches each row from the cursor and unpacks the returned - tuple into the variables deptno, dname, - loc, which are then printed.

    - -

    Run the script in a terminal window:

    - -
    python query2.py
    - -

    The output is:

    - -
    Department number:  10
    -Department name:  ACCOUNTING
    -Department location: NEW YORK
    -Department number:  20
    -Department name:  RESEARCH
    -Department location: DALLAS
    -Department number:  30
    -Department name:  SALES
    -Department location: CHICAGO
    -Department number:  40
    -Department name:  OPERATIONS
    -Department location: BOSTON
    - -
  • - -
  • 3.2 Using fetchone()

    - -

    When the number of rows is large, the fetchall() - call may use too much memory.

    - -

    Review the code contained in query_one.py:

    - -
    -import cx_Oracle
    -import db_config
    -
    -con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn)
    -cur = con.cursor()
    -
    -cur.execute("select * from dept order by deptno")
    -row = cur.fetchone()
    -print(row)
    -
    -row = cur.fetchone()
    -print(row)
    -
    - -

    This uses the fetchone() method to return just a single row as a - tuple. When called multiple time, consecutive rows are returned:

    - -

    Run the script in a terminal window:

    - -
    python query_one.py
    - -

    The first two rows of the table are printed.

    - -
  • - -
  • 3.3 Using fetchmany()

    - -

    Review the code contained in query_many.py:

    - -
    -import cx_Oracle
    -import db_config
    -
    -con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn)
    -cur = con.cursor()
    -
    -cur.execute("select * from dept order by deptno")
    -res = cur.fetchmany(numRows = 3)
    -print(res)
    -
    - -

    The fetchmany() method returns a list of tuples. By - default the number of rows returned is specified by the cursor - attribute arraysize (which defaults to 100). Here the - numRows parameter specifies that three rows should be - returned.

    - -

    Run the script in a terminal window:

    - -
    python query_many.py
    - -

    The first three rows of the table are returned as a list - (Python's name for an array) of tuples.

    - -

    You can access elements of the lists by position indexes. To see this, - edit the file and add:

    - -
    -print(res[0])    # first row
    -print(res[0][1]) # second element of first row
    -
    - -
  • - -
  • 3.4 Scrollable cursors

    - -

    Scrollable cursors enable the application to move backwards as - well as forwards in query results. They can be used to skip rows - as well as move to a particular row.

    - -

    Review the code contained in query_scroll.py:

    - -
    -import cx_Oracle
    -import db_config
    -
    -con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn)
    -cur = con.cursor(scrollable = True)
    -
    -cur.execute("select * from dept order by deptno")
    -
    -cur.scroll(2, mode = "absolute")  # go to second row
    -print(cur.fetchone())
    -
    -cur.scroll(-1)                    # go back one row
    -print(cur.fetchone())
    -
    - -

    Run the script in a terminal window:

    - -
    python query_scroll.py
    - -

    Edit query_scroll.py and experiment with different - scroll options and orders, such as:

    - -
    cur.scroll(1)  # go to next row
    -print(cur.fetchone())
    -
    -cur.scroll(mode = "first")  # go to first row
    -print(cur.fetchone())
    -
    - -

    Try some scroll options that go beyond the number of rows in - the resultset.

    - -
  • - -
  • 3.5 Tuning with arraysize and prefetchrows

    - -

    This section demonstrates a way to improve query performance by increasing - the number of rows returned in each batch from Oracle to the Python - program.

    - -

    Row prefetching and array fetching are both internal buffering techniques - to reduce round-trips to the database. The difference is the code layer that - is doing the buffering, and when the buffering occurs.

    - -

    First, create a table with a large number of rows. - Review query_arraysize.sql:

    - -
    -create table bigtab (mycol varchar2(20));
    -begin
    -  for i in 1..20000
    -  loop
    -   insert into bigtab (mycol) values (dbms_random.string('A',20));
    -  end loop;
    -end;
    -/
    -show errors
    -
    -commit;
    -
    - -

    In a terminal window run the script as:

    - -
    sqlplus /nolog @query_arraysize.sql
    - -

    Review the code contained in query_arraysize.py:

    - -
    -import cx_Oracle
    -import time
    -import db_config
    -
    -con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn)
    -
    -start = time.time()
    -
    -cur = con.cursor()
    -cur.prefetchrows = 100
    -cur.arraysize = 100
    -cur.execute("select * from bigtab")
    -res = cur.fetchall()
    -# print(res)  # uncomment to display the query results
    -
    -elapsed = (time.time() - start)
    -print(elapsed, "seconds")
    -
    - -

    This uses the 'time' module to measure elapsed time of the query. The - prefetchrows and arraysize values are 100. This causes batches of 100 - records at a time to be returned from the database to a cache in Python. - These values can be tuned to reduce the number of "round-trips" - made to the database, often reducing network load and reducing the number of - context switches on the database server. The fetchone(), - fetchmany() and fetchall() methods will read from - the cache before requesting more data from the database.

    - -

    In a terminal window, run:

    - -
    python query_arraysize.py
    - -

    Rerun a few times to see the average times.

    - -

    Experiment with different prefetchrows and arraysize values. For - example, edit query_arraysize.py and change the arraysize - to:

    - -
    cur.arraysize = 2000
    - -

    Rerun the script to compare the performance of different - arraysize settings.

    - -

    In general, larger array sizes improve performance. Depending on how - fast your system is, you may need to use different values than those - given here to see a meaningful time difference.

    - -

    There is a time/space tradeoff for increasing the values. Larger values - will require more memory in Python for buffering the records.

    - -

    If you know the query returns a fixed number of rows, for example 20 - rows, then set arraysize to 20 and prefetchrows to 21. The addition of one - for prefetchrows prevents a round-trip to check for end-of-fetch. The - statement execution and fetch will take a total of one round-trip. This - minimizes load on the database.

    - -

    If you know a query only returns a few records, - decrease the arraysize from the default to reduce memory - usage.

    -
  • -
- -

4. Binding Data

- -

Bind variables enable you to re-execute statements with new data values - without the overhead of re-parsing the statement. Binding improves code - reusability, improves application scalability, and can reduce the risk of SQL - injection attacks. Using bind variables is strongly recommended. - Documentation link for further reading: Using - Bind Variables.

- -
    - -
  • 4.1 Binding in queries

    - -

    Review the code contained in bind_query.py:

    - -
    -import cx_Oracle
    -import db_config
    -
    -con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn)
    -cur = con.cursor()
    -
    -sql = "select * from dept where deptno = :id order by deptno"
    -
    -cur.execute(sql, id = 20)
    -res = cur.fetchall()
    -print(res)
    -
    -cur.execute(sql, id = 10)
    -res = cur.fetchall()
    -print(res)
    -
    - -

    The statement contains a bind variable ":id" placeholder. - The statement is executed twice with different values for the - WHERE clause.

    - -

    From a terminal window, run:

    - -
    python bind_query.py
    - -

    The output shows the details for the two departments.

    - -

    An arbitrary number of named arguments can be used in an - execute() call. Each argument name must match a bind - variable name. Alternatively, instead of passing multiple arguments you - could pass a second argument to execute() that is a sequence - or a dictionary. Later examples show these syntaxes.

    - -

    To bind a database NULL, use the Python value None

    - -

    cx_Oracle uses Oracle Database's Statement Cache. As long as the - statement you pass to execute() is in that cache, you can use - different bind values and still avoid a full statement parse. The - statement cache size is configurable for each connection. To see the - default statement cache size, edit bind_query.py and add a - line at the end:

    - -
    -print(con.stmtcachesize)
    -
    - -

    Re-run the file.

    - -

    In your applications you would set the statement cache size to the - number of unique statements commonly executed.

    - -
  • - -
  • 4.2 Binding in inserts

    - -

    Review the code in bind_insert.sql creating a table - for inserting data:

    - -
    -create table mytab (id number, data varchar2(20), constraint my_pk primary key (id));
    -
    - -

    Run the script as:

    - -
    sqlplus /nolog @bind_insert.sql
    - -

    Review the code contained in bind_insert.py:

    - -
    -import cx_Oracle
    -import db_config
    -
    -con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn)
    -cur = con.cursor()
    -
    -rows = [ (1, "First" ), (2, "Second" ),
    -         (3, "Third" ), (4, "Fourth" ),
    -         (5, "Fifth" ), (6, "Sixth" ),
    -         (7, "Seventh" ) ]
    -
    -cur.executemany("insert into mytab(id, data) values (:1, :2)", rows)
    -
    -# Now query the results back
    -
    -cur2 = con.cursor()
    -cur2.execute('select * from mytab')
    -res = cur2.fetchall()
    -print(res)
    -
    - -

    The 'rows' array contains the data to be inserted.

    - -

    The executemany() call inserts all rows. This - call uses "array binding", which is an efficient way to - insert multiple records.

    - -

    The final part of the script queries the results back and displays them as a list of tuples.

    - -

    From a terminal window, run:

    - -
    python bind_insert.py
    - -

    The new results are automatically rolled back at the end of - the script so re-running it will always show the same number of - rows in the table.

    - -
  • - -
  • 4.3 Batcherrors

    - -

    The Batcherrors features allows invalid data to be identified - while allowing valid data to be inserted.

    - -

    Edit the data values in bind_insert.py and - create a row with a duplicate key:

    - -
    -rows = [ (1, "First" ), (2, "Second" ),
    -         (3, "Third" ), (4, "Fourth" ),
    -         (5, "Fifth" ), (6, "Sixth" ),
    -         (6, "Duplicate" ),
    -         (7, "Seventh" ) ]
    -
    -
    - -

    From a terminal window, run:

    - -
    python bind_insert.py
    - -

    The duplicate generates the error "ORA-00001: unique - constraint (PYTHONHOL.MY_PK) violated". The data is rolled back - and the query returns no rows.

    - -

    Edit the file again and enable batcherrors like:

    - -
    -import cx_Oracle
    -import db_config
    -
    -con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn)
    -cur = con.cursor()
    -
    -rows = [ (1, "First" ), (2, "Second" ),
    -         (3, "Third" ), (4, "Fourth" ),
    -         (5, "Fifth" ), (6, "Sixth" ),
    -         (6, "Duplicate" ),
    -         (7, "Seventh" ) ]
    -
    -cur.executemany("insert into mytab(id, data) values (:1, :2)", rows, batcherrors = True)
    -
    -for error in cur.getbatcherrors():
    -    print("Error", error.message.rstrip(), "at row offset", error.offset)
    -
    -# Now query the results back
    -
    -cur2 = con.cursor()
    -cur2.execute('select * from mytab')
    -res = cur2.fetchall()
    -print(res)
    -
    - -

    Run the file:

    - -
    python bind_insert.py
    - -

    The new code shows the offending duplicate row: "ORA-00001: - unique constraint (PYTHONHOL.MY_PK) violated at row offset 6". - This indicates the 6th data value (counting from 0) had a - problem.

    - -

    The other data gets inserted and is queried back.

    - -

    At the end of the script, cx_Oracle will roll back an uncommitted transaction. If you want to commit results, you can use:

    - -
    con.commit()
    - -

    To force cx_Oracle to roll back, use:

    - -
    con.rollback()
    - -
  • - -
  • 4.4 Binding named objects

    - -

    cx_Oracle can fetch and bind named object types such as Oracle's - Spatial Data Objects (SDO).

    - -

    In a terminal window, start SQL*Plus using the lab credentials and connection string, such as:

    - -
    -sqlplus -l pythonhol/welcome@localhost/orclpdb1
    -
    - -

    Use the SQL*Plus DESCRIBE command to look at the SDO definition:

    - -
    -desc MDSYS.SDO_GEOMETRY
    -
    - -

    It contains various attributes and methods. The top level description is:

    - -
    - Name                                      Null?    Type
    - ----------------------------------------- -------- ----------------------------
    - SDO_GTYPE                                          NUMBER
    - SDO_SRID                                           NUMBER
    - SDO_POINT                                          MDSYS.SDO_POINT_TYPE
    - SDO_ELEM_INFO                                      MDSYS.SDO_ELEM_INFO_ARRAY
    - SDO_ORDINATES                                      MDSYS.SDO_ORDINATE_ARRAY
    -
    - -

    Review the code contained in bind_sdo.py:

    - -
    -import cx_Oracle
    -import db_config
    -
    -con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn)
    -cur = con.cursor()
    -
    -# Create table
    -cur.execute("""begin
    -                 execute immediate 'drop table testgeometry';
    -                 exception when others then
    -                   if sqlcode <> -942 then
    -                     raise;
    -                   end if;
    -               end;""")
    -cur.execute("""create table testgeometry (
    -               id number(9) not null,
    -               geometry MDSYS.SDO_GEOMETRY not null)""")
    -
    -# Create and populate Oracle objects
    -typeObj = con.gettype("MDSYS.SDO_GEOMETRY")
    -elementInfoTypeObj = con.gettype("MDSYS.SDO_ELEM_INFO_ARRAY")
    -ordinateTypeObj = con.gettype("MDSYS.SDO_ORDINATE_ARRAY")
    -obj = typeObj.newobject()
    -obj.SDO_GTYPE = 2003
    -obj.SDO_ELEM_INFO = elementInfoTypeObj.newobject()
    -obj.SDO_ELEM_INFO.extend([1, 1003, 3])
    -obj.SDO_ORDINATES = ordinateTypeObj.newobject()
    -obj.SDO_ORDINATES.extend([1, 1, 5, 7])
    -print("Created object", obj)
    -
    -# Add a new row
    -print("Adding row to table...")
    -cur.execute("insert into testgeometry values (1, :objbv)", objbv = obj)
    -print("Row added!")
    -
    -# Query the row
    -print("Querying row just inserted...")
    -cur.execute("select id, geometry from testgeometry");
    -for row in cur:
    -    print(row)
    -
    - -

    This uses gettype() to get the database types of the -SDO and its object attributes. The newobject() calls -create Python representations of those objects. The python object -atributes are then set. Oracle VARRAY types such as -SDO_ELEM_INFO_ARRAY are set with extend().

    - -

    Run the file:

    - -
    python bind_sdo.py
    - -

    The new SDO is shown as an object, similar to:

    - -
    (1, <cx_Oracle.Object MDSYS.SDO_GEOMETRY at 0x104a76230>)
    - -

    To show the attribute values, edit the the query code section at -the end of the file. Add a new method that traverses the object. The -file below the existing comment "# (Change below here)") -should look like:

    - -
    -# (Change below here)
    -
    -# Define a function to dump the contents of an Oracle object
    -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, "}")
    -
    -# Query the row
    -print("Querying row just inserted...")
    -cur.execute("select id, geometry from testgeometry")
    -for id, obj in cur:
    -    print("Id: ", id)
    -    dumpobject(obj)
    -
    - -

    Run the file again:

    - -
    python bind_sdo.py
    - -

    This shows

    -
    -Querying row just inserted...
    -Id:  1
    -   {
    -    SDO_GTYPE : 2003
    -    SDO_SRID : None
    -    SDO_POINT : None
    -    SDO_ELEM_INFO :
    -       [
    -         1
    -         1003
    -         3
    -       ]
    -    SDO_ORDINATES :
    -       [
    -         1
    -         1
    -         5
    -         7
    -       ]
    -   }
    -
    - -

    To explore further, try setting the SDO attribute SDO_POINT, which -is of type SDO_POINT_TYPE.

    - -

    The gettype() and newobject() methods can -also be used to bind PL/SQL Records and Collections.

    - -

    Before deciding to use objects, review your performance goals because -working with scalar values can be faster.

    - -
  • -
- -

5. PL/SQL

- -

PL/SQL is Oracle's procedural language extension to SQL. PL/SQL - procedures and functions are stored and run in the database. Using - PL/SQL lets all database applications reuse logic, no matter how the - application accesses the database. Many data-related operations can - be performed in PL/SQL faster than extracting the data into a - program (for example, Python) and then processing it. Documentation link - for further reading: PL/SQL Execution.

- -
    -
  • 5.1 PL/SQL functions

    - -

    Review plsql_func.sql which creates a PL/SQL - stored function myfunc() to insert a row into a new - table named ptab and return double the inserted - value:

    - -
    -create table ptab (mydata varchar(20), myid number);
    -
    -create or replace function myfunc(d_p in varchar2, i_p in number) return number as
    -  begin
    -    insert into ptab (mydata, myid) values (d_p, i_p);
    -    return (i_p * 2);
    -  end;
    -/
    -
    - -

    Run the script using:

    - -
    sqlplus /nolog @plsql_func.sql
    - -

    Review the code contained in plsql_func.py:

    - -
    -import cx_Oracle
    -import db_config
    -
    -con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn)
    -cur = con.cursor()
    -
    -res = cur.callfunc('myfunc', int, ('abc', 2))
    -print(res)
    -
    - -

    This uses callfunc() to execute the function. - The second parameter is the type of the returned value. It should be one - of the types supported by cx_Oracle or one of the type constants defined - by cx_Oracle (such as cx_Oracle.NUMBER). The two PL/SQL function - parameters are passed as a tuple, binding them to the function parameter - arguments.

    - -

    From a terminal window, run:

    - -
    python plsql_func.py
    - -

    The output is a result of the PL/SQL function calculation.

    - -
  • - -
  • 5.2 PL/SQL procedures

    - -

    Review plsql_proc.sql which creates a PL/SQL procedure -myproc() to accept two parameters. The second parameter -contains an OUT return value.

    - -
    -create or replace procedure myproc(v1_p in number, v2_p out number) as
    -begin
    -  v2_p := v1_p * 2;
    -end;
    -/
    -
    - -

    Run the script with:

    - -
    sqlplus /nolog @plsql_proc.sql
    - -

    Review the code contained in plsql_proc.py:

    - -
    -import cx_Oracle
    -import db_config
    -
    -con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn)
    -cur = con.cursor()
    -
    -myvar = cur.var(int)
    -cur.callproc('myproc', (123, myvar))
    -print(myvar.getvalue())
    -
    - -

    This creates an integer variable myvar to hold - the value returned by the PL/SQL OUT parameter. The input number - 123 and the output variable name are bound to the procedure call - parameters using a tuple.

    - -

    To call the PL/SQL procedure, the callproc() - method is used.

    - -

    In a terminal window, run:

    - -
    python plsql_proc.py
    - -

    The getvalue() method displays the returned - value.

    -
  • -
- -

6. Type Handlers

- -

Type handlers enable applications to alter data that is fetched from, or sent -to, the database. Documentation links for further reading: Changing Fetched Data Types with Output Type Handlers and Changing Bind Data Types using an Input Type Handler.

- -
    -
  • -

    6.1 Basic output type handler

    - -

    Output type handlers enable applications to change how data - is fetched from the database. For example, numbers can be - returned as strings or decimal objects. LOBs can be returned as - string or bytes.

    - -

    A type handler is enabled by setting the - outputtypehandler attribute on either a cursor or - the connection. If set on a cursor it only affects queries executed - by that cursor. If set on a connection it affects all queries executed - on cursors created by that connection.

    - -

    Review the code contained in type_output.py:

    - -
    -import cx_Oracle
    -import db_config
    -
    -con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn)
    -cur = con.cursor()
    -
    -print("Standard output...")
    -for row in cur.execute("select * from dept"):
    -    print(row)
    -
    - -

    In a terminal window, run:

    - -
    python type_output.py
    - -

    This shows the department number represented as digits like - 10.

    - -

    Add an output type handler to the bottom of the file:

    - -
    -def ReturnNumbersAsStrings(cursor, name, defaultType, size, precision, scale):
    -    if defaultType == cx_Oracle.NUMBER:
    -        return cursor.var(str, 9, cursor.arraysize)
    -
    -print("Output type handler output...")
    -cur = con.cursor()
    -cur.outputtypehandler = ReturnNumbersAsStrings
    -for row in cur.execute("select * from dept"):
    -    print(row)
    -
    - -

    This type handler converts any number columns to strings with - maxium size 9.

    - -

    Run the script again:

    - -
    python type_output.py
    - -

    The new output shows the department numbers are now strings - within quotes like '10'.

    - -
  • - -
  • 6.2 Output type handlers and variable converters

    - -

    When numbers are fetched from the database, the conversion - from Oracle's decimal representation to Python's binary format - may need careful handling. To avoid unexpected issues, the - general recommendation is to do number operations in SQL or - PL/SQL, or to use the decimal module in Python.

    - -

    Output type handlers can be combined with variable converters - to change how data is fetched.

    - -

    Review type_converter.py:

    - -
    -import cx_Oracle
    -import db_config
    -
    -con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn)
    -cur = con.cursor()
    -
    -for value, in cur.execute("select 0.1 from dual"):
    -    print("Value:", value, "* 3 =", value * 3)
    -
    - -

    Run the file:

    - -
    python type_converter.py
    - -

    The output is like:

    - -
    Value: 0.1 * 3 = 0.30000000000000004
    - -

    Edit the file and add a type handler that uses a Python - decimal converter:

    - -
    -import cx_Oracle
    -import decimal
    -import db_config
    -
    -con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn)
    -cur = con.cursor()
    -
    -def ReturnNumbersAsDecimal(cursor, name, defaultType, size, precision, scale):
    -    if defaultType == cx_Oracle.NUMBER:
    -        return cursor.var(str, 9, cursor.arraysize, outconverter = decimal.Decimal)
    -
    -cur.outputtypehandler = ReturnNumbersAsDecimal
    -
    -for value, in cur.execute("select 0.1 from dual"):
    -    print("Value:", value, "* 3 =", value * 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.

    - -

    Run the file again:

    - -
    python type_converter.py
    - -

    Output is like:

    - -
    Value: 0.1 * 3 = 0.3
    - -

    Although the code demonstrates the use of outconverter, in this - particular case, the variable can be created simply by using the - following code to replace the outputtypehandler function defined - above:

    - -
    -def ReturnNumbersAsDecimal(cursor, name, defaultType, size, precision, scale):
    -    if defaultType == cx_Oracle.NUMBER:
    -        return cursor.var(decimal.Decimal, arraysize = cursor.arraysize)
    -
    - -
  • - -
  • 6.3 Input type handlers

    - -

    Input type handlers enable applications to change how data is - bound to statements, or to enable new types to be bound directly - without having to be converted individually.

    - -

    Review type_input.py, which is similar to the - final bind_sdo.py from section 4.4, with the - addition of a new class and converter (shown in bold):

    - -
    -import cx_Oracle
    -import db_config
    -
    -con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn)
    -cur = con.cursor()
    -
    -# Create table
    -cur.execute("""begin
    -                 execute immediate 'drop table testgeometry';
    -                 exception when others then
    -                   if sqlcode <> -942 then
    -                     raise;
    -                   end if;
    -               end;""")
    -cur.execute("""create table testgeometry (
    -               id number(9) not null,
    -               geometry MDSYS.SDO_GEOMETRY not null)""")
    -
    -# Create a Python class for an SDO
    -class mySDO(object):
    -
    -    def __init__(self, gtype, elemInfo, ordinates):
    -        self.gtype = gtype
    -        self.elemInfo = elemInfo
    -        self.ordinates = ordinates
    -
    -# Get Oracle type information
    -objType = con.gettype("MDSYS.SDO_GEOMETRY")
    -elementInfoTypeObj = con.gettype("MDSYS.SDO_ELEM_INFO_ARRAY")
    -ordinateTypeObj = con.gettype("MDSYS.SDO_ORDINATE_ARRAY")
    -
    -# Convert a Python object to MDSYS.SDO_GEOMETRY
    -def SDOInConverter(value):
    -    obj = objType.newobject()
    -    obj.SDO_GTYPE = value.gtype
    -    obj.SDO_ELEM_INFO = elementInfoTypeObj.newobject()
    -    obj.SDO_ELEM_INFO.extend(value.elemInfo)
    -    obj.SDO_ORDINATES = ordinateTypeObj.newobject()
    -    obj.SDO_ORDINATES.extend(value.ordinates)
    -    return obj
    -
    -def SDOInputTypeHandler(cursor, value, numElements):
    -    if isinstance(value, mySDO):
    -        return cursor.var(cx_Oracle.OBJECT, arraysize = numElements,
    -                inconverter = SDOInConverter, typename = objType.name)
    -
    -sdo = mySDO(2003, [1, 1003, 3], [1, 1, 5, 7])  # Python object
    -cur.inputtypehandler = SDOInputTypeHandler
    -cur.execute("insert into testgeometry values (:1, :2)", (1, sdo))
    -
    -# Define a function to dump the contents of an Oracle object
    -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, "}")
    -
    -# Query the row
    -print("Querying row just inserted...")
    -cur.execute("select id, geometry from testgeometry")
    -for (id, obj) in cur:
    -    print("Id: ", id)
    -    dumpobject(obj)
    -
    - -

    In the new file, a Python class mySDO is defined, -which has attributes corresponding to each Oracle MDSYS.SDO_GEOMETRY -attribute. - -The mySDO class is used lower in the code to create a -Python instance:

    - -
    -sdo = mySDO(2003, [1, 1003, 3], [1, 1, 5, 7])
    - -

    which is then directly bound into the INSERT statement like:

    - -
    cur.execute("insert into testgeometry values (:1, :2)", (1, sdo))
    - -

    The mapping between Python and Oracle objects is handled in -SDOInConverter which uses the cx_Oracle -newobject() and extend() methods to create -an Oracle object from the Python object values. The -SDOInConverter method is called by the input type handler -SDOInputTypeHandler whenever an instance of -mySDO is inserted with the cursor.

    - -

    To confirm the behavior, run the file:

    - -
    python type_input.py
    - -
  • - -
- -

7. LOBs

- -

Oracle Database "LOB" long objects can be streamed using a LOB - locator, or worked with directly as strings or bytes. Documentation link - for further reading: Using CLOB and BLOB Data.

- -
    -
  • -

    7.1 Fetching a CLOB using a locator

    - -

    Review the code contained in clob.py:

    - -
    -import cx_Oracle
    -import db_config
    -
    -con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn)
    -cur = con.cursor()
    -
    -print("Inserting data...")
    -cur.execute("truncate table testclobs")
    -longString = ""
    -for i in range(5):
    -    char = chr(ord('A') + i)
    -    longString += char * 250
    -    cur.execute("insert into testclobs values (:1, :2)",
    -                   (i + 1, "String data " + longString + ' End of string'))
    -con.commit()
    -
    -print("Querying data...")
    -cur.execute("select * from testclobs where id = :id", {'id': 1})
    -(id, clob) = cur.fetchone()
    -print("CLOB length:", clob.size())
    -clobdata = clob.read()
    -print("CLOB data:", clobdata)
    -
    - -

    This inserts some test string data and then fetches one - record into clob, which is a cx_Oracle character - LOB Object. Methods on LOB include size() and - read().

    - -

    To see the output, run the file:

    - -
    python clob.py
    - -

    Edit the file and experiment reading chunks of data by giving - start character position and length, such as - clob.read(1,10)

    - -
  • - -
  • -

    7.2 Fetching a CLOB as a string

    - -

    For CLOBs small enough to fit in the application memory, it - is much faster to fetch them directly as strings.

    - -

    Review the code contained in clob_string.py. - The differences from clob.py are shown in bold:

    - -
    -import cx_Oracle
    -import db_config
    -
    -con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn)
    -cur = con.cursor()
    -
    -print("Inserting data...")
    -cur.execute("truncate table testclobs")
    -longString = ""
    -for i in range(5):
    -    char = chr(ord('A') + i)
    -    longString += char * 250
    -    cur.execute("insert into testclobs values (:1, :2)",
    -                (i + 1, "String data " + longString + ' End of string'))
    -con.commit()
    -
    -def OutputTypeHandler(cursor, name, defaultType, size, precision, scale):
    -    if defaultType == cx_Oracle.CLOB:
    -        return cursor.var(cx_Oracle.LONG_STRING, arraysize = cursor.arraysize)
    -
    -con.outputtypehandler = OutputTypeHandler
    -
    -print("Querying data...")
    -cur.execute("select * from testclobs where id = :id", {'id': 1})
    -(id, clobdata) = cur.fetchone()
    -print("CLOB length:", len(clobdata))
    -print("CLOB data:", clobdata)
    -
    - -

    The OutputTypeHandler causes cx_Oracle to fetch the CLOB as a - string. Standard Python string functions such as - len() can be used on the result.

    - -

    The output is the same as for clob.py. To - check, run the file:

    - -
    python clob_string.py
    - -
  • -
- -

8. Rowfactory functions

- -

Rowfactory functions enable queries to return objects other than - tuples. They can be used to provide names for the various columns - or to return custom objects.

- -
    -
  • 8.1 Rowfactory for mapping column names

    - -

    Review the code contained in rowfactory.py:

    - -
    -import collections
    -import cx_Oracle
    -import db_config
    -
    -con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn)
    -cur = con.cursor()
    -
    -cur.execute("select deptno, dname from dept")
    -rows = cur.fetchall()
    -
    -print('Array indexes:')
    -for row in rows:
    -    print(row[0], "->", row[1])
    -
    -print('Loop target variables:')
    -for c1, c2 in rows:
    -    print(c1, "->", c2)
    -
    - -

    This shows two methods of accessing result set items from a data - row. The first uses array indexes like row[0]. The - second uses loop target variables which take the values of each row - tuple.

    - -

    Run the file:

    - -
    python rowfactory.py
    - -

    Both access methods gives the same results.

    - -

    To use a rowfactory function, edit rowfactory.py and - add this code at the bottom:

    - -
    -print('Rowfactory:')
    -cur.execute("select deptno, dname from dept")
    -cur.rowfactory = collections.namedtuple("MyClass", ["DeptNumber", "DeptName"])
    -
    -rows = cur.fetchall()
    -for row in rows:
    -    print(row.DeptNumber, "->", row.DeptName)
    -
    - -

    This uses the Python factory function - namedtuple() to create a subclass of tuple that allows - access to the elements via indexes or the given field names.

    - -

    The print() function shows the use of the new - named tuple fields. This coding style can help reduce coding - errors.

    - -

    Run the script again:

    - -
    python rowfactory.py
    - - -

    The output results are the same.

    - -
  • -
- -

9. Subclassing connections and cursors

- -

Subclassing enables application to "hook" connection and cursor - creation. This can be used to alter or log connection and execution - parameters, and to extend cx_Oracle functionality. Documentation link for - further reading: Tracing SQL and PL/SQL Statements.

- -
    -
  • 9.1 Subclassing connections

    - -

    Review the code contained in subclass.py:

    - -
    -import cx_Oracle
    -import db_config
    -
    -class MyConnection(cx_Oracle.Connection):
    -
    -    def __init__(self):
    -        print("Connecting to database")
    -        return super(MyConnection, self).__init__(db_config.user, db_config.pw, db_config.dsn)
    -
    -con = MyConnection()
    -cur = con.cursor()
    -
    -cur.execute("select count(*) from emp where deptno = :bv", (10,))
    -count, = cur.fetchone()
    -print("Number of rows:", count)
    -
    - -

    This creates a new class "MyConnection" that inherits from the - cx_Oracle Connection class. The __init__ method is - invoked when an instance of the new class is created. It prints a - message and calls the base class, passing the connection - credentials.

    - -

    In the "normal" application, the application code:

    - -
    con = MyConnection()
    - -

    does not need to supply any credentials, as they are embedded in the - custom subclass. All the cx_Oracle methods such as cursor() are - available, as shown by the query.

    - -

    Run the file:

    - -
    python subclass.py
    - -

    The query executes successfully.

    - -
  • - -
  • 9.2 Subclassing cursors

    - -

    Edit subclass.py and extend the - cursor() method with a new MyCursor class:

    - -
    -import cx_Oracle
    -import db_config
    -
    -class MyConnection(cx_Oracle.Connection):
    -
    -    def __init__(self):
    -        print("Connecting to database")
    -        return super(MyConnection, self).__init__(db_config.user, db_config.pw, db_config.dsn)
    -
    -    def cursor(self):
    -        return MyCursor(self)
    -
    -class MyCursor(cx_Oracle.Cursor):
    -
    -   def execute(self, statement, args):
    -       print("Executing:", statement)
    -       print("Arguments:")
    -       for argIndex, arg in enumerate(args):
    -           print("  Bind", argIndex + 1, "has value", repr(arg))
    -           return super(MyCursor, self).execute(statement, args)
    -
    -   def fetchone(self):
    -       print("Fetchone()")
    -       return super(MyCursor, self).fetchone()
    -
    -con = MyConnection()
    -cur = con.cursor()
    -
    -cur.execute("select count(*) from emp where deptno = :bv", (10,))
    -count, = cur.fetchone()
    -print("Number of rows:", count)
    -
    - -

    When the application gets a cursor from the -MyConnection class, the new cursor() method -returns an instance of our new MyCursor class.

    - -

    The "application" query code remains unchanged. The new -execute() and fetchone() methods of the -MyCursor class get invoked. They do some logging and -invoke the parent methods to do the actual statement execution.

    - -

    To confirm this, run the file again:

    - -
    python subclass.py
    - -
  • - -
- -

10. Advanced Queuing

- -

Oracle Advanced Queuing (AQ) allows messages to be passed between -applications. Documentation link for further reading: Oracle -Advanced Queuing (AQ).

- -
    -
  • 10.1 Message passing with Oracle Advanced Queuing

    - -

    Review aq.py:

    - -
    -import cx_Oracle
    -import decimal
    -import db_config
    -
    -con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn)
    -cur = con.cursor()
    -
    -BOOK_TYPE_NAME = "UDT_BOOK"
    -QUEUE_NAME = "BOOKS"
    -QUEUE_TABLE_NAME = "BOOK_QUEUE_TABLE"
    -
    -# Cleanup
    -cur.execute(
    -    """begin
    -         dbms_aqadm.stop_queue('""" + QUEUE_NAME + """');
    -         dbms_aqadm.drop_queue('""" + QUEUE_NAME + """');
    -         dbms_aqadm.drop_queue_table('""" + QUEUE_TABLE_NAME + """');
    -         execute immediate 'drop type """ + BOOK_TYPE_NAME + """';
    -         exception when others then
    -           if sqlcode <> -24010 then
    -             raise;
    -           end if;
    -       end;""")
    -
    -# Create a type
    -print("Creating books type UDT_BOOK...")
    -cur.execute("""
    -        create type %s as object (
    -            title varchar2(100),
    -            authors varchar2(100),
    -            price number(5,2)
    -        );""" % BOOK_TYPE_NAME)
    -
    -# Create queue table and queue and start the queue
    -print("Creating queue table...")
    -cur.callproc("dbms_aqadm.create_queue_table",
    -        (QUEUE_TABLE_NAME, BOOK_TYPE_NAME))
    -cur.callproc("dbms_aqadm.create_queue", (QUEUE_NAME, QUEUE_TABLE_NAME))
    -cur.callproc("dbms_aqadm.start_queue", (QUEUE_NAME,))
    -
    -booksType = con.gettype(BOOK_TYPE_NAME)
    -queue = con.queue(QUEUE_NAME, booksType)
    -
    -# Enqueue a few messages
    -print("Enqueuing messages...")
    -
    -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"))
    -]
    -
    -for title, authors, price in BOOK_DATA:
    -    book = booksType.newobject()
    -    book.TITLE = title
    -    book.AUTHORS = authors
    -    book.PRICE = price
    -    print(title)
    -    queue.enqOne(con.msgproperties(payload=book))
    -    con.commit()
    -
    -# Dequeue the messages
    -print("\nDequeuing messages...")
    -queue.deqOptions.wait = cx_Oracle.DEQ_NO_WAIT
    -while True:
    -    props = queue.deqOne()
    -    if not props:
    -        break
    -    print(props.payload.TITLE)
    -    con.commit()
    -
    -print("\nDone.")
    -
    - -

    This file sets up Advanced Queuing using Oracle's DBMS_AQADM -package. The queue is used for passing Oracle UDT_BOOK objects. The -file uses AQ interface features enhanced in cx_Oracle 7.2.

    - -

    Run the file:

    - -
    python aq.py
    - -

    The output shows messages being queued and dequeued.

    - -

    To experiment, split the code into three files: one to create and -start the queue, and two other files to queue and dequeue messages. -Experiment running the queue and dequeue files concurrently in -separate terminal windows.

    - -

    Try removing the commit() call in -aq-dequeue.py. Now run aq-enqueue.py once -and then aq-dequeue.py several times. The same messages -will be available each time you try to dequeue them.

    - -

    Change aq-dequeue.py to commit in a separate -transaction by changing the "visibility" setting:

    - -
    -queue.deqOptions.visibility = cx_Oracle.DEQ_IMMEDIATE
    -
    - -

    This gives the same behavior as the original code.

    - -

    Now change the options of enqueued messages so that they expire from the -queue if they have not been dequeued after four seconds:

    - -
    -queue.enqOne(con.msgproperties(payload=book, expiration=4))
    -
    - -

    Now run aq-enqueue.py and wait four seconds before you -run aq-dequeue.py. There should be no messages to -dequeue.

    - -

    If you are stuck, look in the solutions directory at -the aq-dequeue.py, aq-enqueue.py and -aq-queuestart.py files.

    - -
  • -
- -

11. Simple Oracle Document Access (SODA)

- -

Simple Oracle Document Access (SODA) is a set of NoSQL-style APIs. - Documents can be inserted, queried, and retrieved from Oracle - Database. By default, documents are JSON strings. SODA APIs - exist in many languages. Documentation link for further reading: Simple - Oracle Document Access (SODA).

- -
    - -
  • 11.1 Inserting JSON Documents

    - -

    Review soda.py:

    - -
    -import cx_Oracle
    -import db_config
    -
    -con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn)
    -
    -soda = con.getSodaDatabase()
    -
    -# Explicit metadata is used for maximum version portability
    -metadata = {
    -               "keyColumn": {
    -                   "name":"ID"
    -               },
    -               "contentColumn": {
    -                   "name": "JSON_DOCUMENT",
    -                   "sqlType": "BLOB"
    -               },
    -               "versionColumn": {
    -                   "name": "VERSION",
    -                   "method": "UUID"
    -               },
    -               "lastModifiedColumn": {
    -                   "name": "LAST_MODIFIED"
    -               },
    -               "creationTimeColumn": {
    -                   "name": "CREATED_ON"
    -               }
    -           }
    -
    -collection = soda.createCollection("friends", metadata)
    -
    -content = {'name': 'Jared', 'age': 35, 'address': {'city': 'Melbourne'}}
    -
    -doc = collection.insertOneAndGet(content)
    -key = doc.key
    -
    -doc = collection.find().key(key).getOne()
    -content = doc.getContent()
    -print('Retrieved SODA document dictionary is:')
    -print(content)
    -
    - -

    soda.createCollection() will create a new collection, or - open an existing collection, if the name is already in use. (Due to a - change in the default "sqlType" storage for Oracle Database 21c, the - metadata is explicitly stated to use a BLOB column. This lets the example - run with different client and database versions).

    - -

    insertOneAndGet() inserts the content of a - document into the database and returns a SODA Document Object. - This allows access to meta data such as the document key. By - default, document keys are automatically generated.

    - -

    The find() method is used to begin an operation - that will act upon documents in the collection.

    - -

    content is a dictionary. You can also get a JSON string - by calling doc.getContentAsString().

    - -

    Run the file:

    - -
    python soda.py
    - -

    The output shows the content of the new document.

    - -
  • - -
  • 11.2 Searching SODA Documents

    - -

    Extend soda.py to insert some more documents and - perform a find filter operation:

    - -
    -myDocs = [
    -    {'name': 'Gerald', 'age': 21, 'address': {'city': 'London'}},
    -    {'name': 'David', 'age': 28, 'address': {'city': 'Melbourne'}},
    -    {'name': 'Shawn', 'age': 20, 'address': {'city': 'San Francisco'}}
    -]
    -collection.insertMany(myDocs)
    -
    -filterSpec = { "address.city": "Melbourne" }
    -myDocuments = collection.find().filter(filterSpec).getDocuments()
    -
    -print('Melbourne people:')
    -for doc in myDocuments:
    -    print(doc.getContent()["name"])
    -
    - -

    Run the script again:

    - -
    python soda.py
    - -

    The find operation filters the collection and returns - documents where the city is Melbourne. Note the - insertMany() method is currently in preview.

    - -

    SODA supports query by example (QBE) with an extensive set of - operators. Extend soda.py with a QBE to find - documents where the age is less than 25:

    - -
    -filterSpec = {'age': {'$lt': 25}}
    -myDocuments = collection.find().filter(filterSpec).getDocuments()
    -
    -print('Young people:')
    -for doc in myDocuments:
    -    print(doc.getContent()["name"])
    -
    - -

    Running the script displays the names.

    - -
  • -
- -

Summary

-

In this tutorial, you have learned how to:

-
    -
  • Create connections
  • -
  • Use cx_Oracle connection pooling and Database Resident Connection Pooling
  • -
  • Execute queries and fetch data
  • -
  • Use bind variables
  • -
  • Use PL/SQL stored functions and procedures
  • -
  • Extend cx_Oracle classes
  • -
  • Use Oracle Advanced Queuing
  • -
  • Use the "SODA" document store API
  • -
- -

For further reading see the cx_Oracle - documentation.

- -

Appendix: Python Primer

- -

Python is a dynamically typed scripting language. It is most - often used to run command-line scripts but is also used for web - applications and web services.

- -

Running Python

- -

You can either:

- -
    - -
  • Create a file of Python commands, such as - myfile.py. This can be run with:

    -
    python myfile.py
  • - -
  • Alternatively run the Python interpreter by executing the - python command in a terminal, and then interactively - enter commands. Use Ctrl-D to exit back to the - operating system prompt.

  • - -
- -

When you run scripts, Python automatically creates bytecode - versions of them in a folder called __pycache__. - These improve performance of scripts that are run multiple times. - They are automatically recreated if the source file changes.

- -

Indentation

- -

Whitespace indentation is significant in Python. When copying - examples, use the same column alignment as shown. The samples in - this lab use spaces, not tabs.

- -

The following indentation prints 'done' once after the loop has - completed:

- -
-for i in range(5):
-    print(i)
-print('done')
-
- -

But this indentation prints 'done' in each iteration:

- -
-for i in range(5):
-    print(i)
-    print('done')
-
- -

Strings

- -

Python strings can be enclosed in - single or double quotes:

- -
'A string constant'
-"another constant"
-

Multi line strings use a triple-quote syntax:

-
"""
-SELECT *
-FROM EMP
-"""
- -

Variables

- -

Variables do not need types declared:

-
count = 1
-ename = 'Arnie'
- -

Comments

- -

Comments are either single line:

-
# a short comment
-

They can be multi-line using the triple-quote token to create a string that does nothing:

-
"""
-a longer
-comment
-"""
-
- -

Printing

- -

Strings and variables can be displayed with a print() function:

-
print('Hello, World!')
-print('Value:', count)
- -

Data Structures

- -

Associative arrays are called 'dictionaries':

-
a2 = {'PI':3.1415, 'E':2.7182}
-

Ordered arrays are called 'lists':

-
a3 = [101, 4, 67]
-

Lists can be accessed via indexes.

-
-print(a3[0])
-print(a3[-1])
-print(a3[1:3])
-
- -

Tuples are like lists but cannot be changed once they are - created. They are created with parentheses:

- -
a4 = (3, 7, 10)
- -

Individual values in a tuple can be assigned to variables like:

- -
v1, v2, v3 = a4
- -

Now the variable v1 contains 3, the variable v2 contains 7 and the variable v3 contains 10.

- -

The value in a single entry tuple like "(13,)"can be - assigned to a variable by putting a comma after the variable name - like:

- -
v1, = (13,)
- -

If the assignment is:

- -
v1 = (13,)
- -

then v1 will contain the whole tuple "(13,)"

- -

Objects

- -

Everything in Python is an object. As an example, given the of the -list a3 above, the append() method can be -used to add a value to the list.

- -
a3.append(23)
-

Now a3 contains [101, 4, 67, 23]

- -

Flow Control

- -

Code flow can be controlled with tests and loops. The -if/elif/else statements look -like:

- -
-if v == 2 or v == 4:
-    print('Even')
-elif v == 1 or v == 3:
-    print('Odd')
-else:
-    print('Unknown number')
-
- -

This also shows how the clauses are delimited with colons, and each -sub block of code is indented.

- -

Loops

- -

A traditional loop is:

-
for i in range(10):
-    print(i)
- -

This prints the numbers from 0 to 9. The value of i - is incremented in each iteration.

- -

The 'for' command can also be used to iterate over - lists and tuples:

- -
-a5 = ['Aa', 'Bb', 'Cc']
-for v in a5:
-    print(v)
-
- -

This sets v to each element of the list -a5 in turn.

- -

Functions

- -

A function may be defined as:

- -
-def myfunc(p1, p2):
-    "Function documentation: add two numbers"
-    print(p1, p2)
-    return p1 + p2
- -

Functions may or may not return values. This function could be called using:

- -
v3 = myfunc(1, 3)
- -

Function calls must appear after their function definition.

- -

Functions are also objects and have attributes. The inbuilt -__doc__ attribute can be used to find the function -description:

- -
print(myfunc.__doc__)
- -

Modules

- -

Sub-files can be included in Python scripts with an import statement.

-
import os
-import sys
-

Many predefined modules exist, such as the os and the sys modules.

- - -

Resources

- - - - - - - - - - -
Copyright © 2017, 2021, Oracle and/or its affiliates. All rights reserved
- - - diff --git a/samples/tutorial/aq.py b/samples/tutorial/aq.py deleted file mode 100644 index df036e64..00000000 --- a/samples/tutorial/aq.py +++ /dev/null @@ -1,80 +0,0 @@ -#------------------------------------------------------------------------------ -# aq.py (Section 10.1) -#------------------------------------------------------------------------------ - -#------------------------------------------------------------------------------ -# Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. -#------------------------------------------------------------------------------ - -import cx_Oracle -import decimal -import db_config - -con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn) -cur = con.cursor() - -BOOK_TYPE_NAME = "UDT_BOOK" -QUEUE_NAME = "BOOKS" -QUEUE_TABLE_NAME = "BOOK_QUEUE_TABLE" - -# Cleanup -cur.execute( - """begin - dbms_aqadm.stop_queue('""" + QUEUE_NAME + """'); - dbms_aqadm.drop_queue('""" + QUEUE_NAME + """'); - dbms_aqadm.drop_queue_table('""" + QUEUE_TABLE_NAME + """'); - execute immediate 'drop type """ + BOOK_TYPE_NAME + """'; - exception when others then - if sqlcode <> -24010 then - raise; - end if; - end;""") - -# Create a type -print("Creating books type UDT_BOOK...") -cur.execute(""" - create type %s as object ( - title varchar2(100), - authors varchar2(100), - price number(5,2) - );""" % BOOK_TYPE_NAME) - -# Create queue table and queue and start the queue -print("Creating queue table...") -cur.callproc("dbms_aqadm.create_queue_table", - (QUEUE_TABLE_NAME, BOOK_TYPE_NAME)) -cur.callproc("dbms_aqadm.create_queue", (QUEUE_NAME, QUEUE_TABLE_NAME)) -cur.callproc("dbms_aqadm.start_queue", (QUEUE_NAME,)) - -booksType = con.gettype(BOOK_TYPE_NAME) -queue = con.queue(QUEUE_NAME, booksType) - -# Enqueue a few messages -print("Enqueuing messages...") - -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")) -] - -for title, authors, price in BOOK_DATA: - book = booksType.newobject() - book.TITLE = title - book.AUTHORS = authors - book.PRICE = price - print(title) - queue.enqOne(con.msgproperties(payload=book)) - con.commit() - -# Dequeue the messages -print("\nDequeuing messages...") -queue.deqOptions.wait = cx_Oracle.DEQ_NO_WAIT -while True: - props = queue.deqOne() - if not props: - break - print(props.payload.TITLE) - con.commit() - -print("\nDone.") diff --git a/samples/tutorial/bind_insert.py b/samples/tutorial/bind_insert.py deleted file mode 100644 index f3ec9b45..00000000 --- a/samples/tutorial/bind_insert.py +++ /dev/null @@ -1,27 +0,0 @@ -#------------------------------------------------------------------------------ -# bind_insert.py (Section 4.2 and 4.3) -#------------------------------------------------------------------------------ - -#------------------------------------------------------------------------------ -# Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. -#------------------------------------------------------------------------------ - -import cx_Oracle -import db_config - -con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn) -cur = con.cursor() - -rows = [ (1, "First" ), (2, "Second" ), - (3, "Third" ), (4, "Fourth" ), - (5, "Fifth" ), (6, "Sixth" ), - (7, "Seventh" ) ] - -cur.executemany("insert into mytab(id, data) values (:1, :2)", rows) - -# Now query the results back - -cur2 = con.cursor() -cur2.execute('select * from mytab') -res = cur2.fetchall() -print(res) diff --git a/samples/tutorial/bind_insert.sql b/samples/tutorial/bind_insert.sql deleted file mode 100644 index 5e538f55..00000000 --- a/samples/tutorial/bind_insert.sql +++ /dev/null @@ -1,27 +0,0 @@ -------------------------------------------------------------------------------- --- bind_insert.sql (Section 4.2) -------------------------------------------------------------------------------- - -/*----------------------------------------------------------------------------- - * Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. - *---------------------------------------------------------------------------*/ - -set echo off -@@db_config.sql -set echo on - -connect &user/&pw@&connect_string - -begin - execute immediate 'drop table mytab'; -exception -when others then - if sqlcode not in (-00942) then - raise; - end if; -end; -/ - -create table mytab (id number, data varchar2(20), constraint my_pk primary key (id)); - -exit diff --git a/samples/tutorial/bind_query.py b/samples/tutorial/bind_query.py deleted file mode 100644 index 72d39821..00000000 --- a/samples/tutorial/bind_query.py +++ /dev/null @@ -1,23 +0,0 @@ -#------------------------------------------------------------------------------ -# bind_query.py (Section 4.1) -#------------------------------------------------------------------------------ - -#------------------------------------------------------------------------------ -# Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. -#------------------------------------------------------------------------------ - -import cx_Oracle -import db_config - -con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn) -cur = con.cursor() - -sql = "select * from dept where deptno = :id order by deptno" - -cur.execute(sql, id = 20) -res = cur.fetchall() -print(res) - -cur.execute(sql, id = 10) -res = cur.fetchall() -print(res) diff --git a/samples/tutorial/bind_sdo.py b/samples/tutorial/bind_sdo.py deleted file mode 100644 index 0d52d8cc..00000000 --- a/samples/tutorial/bind_sdo.py +++ /dev/null @@ -1,50 +0,0 @@ -#------------------------------------------------------------------------------ -# bind_sdo.py (Section 4.4) -#------------------------------------------------------------------------------ - -#------------------------------------------------------------------------------ -# Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. -#------------------------------------------------------------------------------ - -import cx_Oracle -import db_config - -con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn) -cur = con.cursor() - -# Create table -cur.execute("""begin - execute immediate 'drop table testgeometry'; - exception when others then - if sqlcode <> -942 then - raise; - end if; - end;""") -cur.execute("""create table testgeometry ( - id number(9) not null, - geometry MDSYS.SDO_GEOMETRY not null)""") - -# Create and populate Oracle objects -typeObj = con.gettype("MDSYS.SDO_GEOMETRY") -elementInfoTypeObj = con.gettype("MDSYS.SDO_ELEM_INFO_ARRAY") -ordinateTypeObj = con.gettype("MDSYS.SDO_ORDINATE_ARRAY") -obj = typeObj.newobject() -obj.SDO_GTYPE = 2003 -obj.SDO_ELEM_INFO = elementInfoTypeObj.newobject() -obj.SDO_ELEM_INFO.extend([1, 1003, 3]) -obj.SDO_ORDINATES = ordinateTypeObj.newobject() -obj.SDO_ORDINATES.extend([1, 1, 5, 7]) -print("Created object", obj) - -# Add a new row -print("Adding row to table...") -cur.execute("insert into testgeometry values (1, :obj)", obj = obj) -print("Row added!") - -# (Change below here) - -# Query the row -print("Querying row just inserted...") -cur.execute("select id, geometry from testgeometry"); -for row in cur: - print(row) diff --git a/samples/tutorial/clob.py b/samples/tutorial/clob.py deleted file mode 100644 index f8dc668e..00000000 --- a/samples/tutorial/clob.py +++ /dev/null @@ -1,30 +0,0 @@ -#------------------------------------------------------------------------------ -# clob.py (Section 7.1) -#------------------------------------------------------------------------------ - -#------------------------------------------------------------------------------ -# Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. -#------------------------------------------------------------------------------ - -import cx_Oracle -import db_config - -con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn) -cur = con.cursor() - -print("Inserting data...") -cur.execute("truncate table testclobs") -longString = "" -for i in range(5): - char = chr(ord('A') + i) - longString += char * 250 - cur.execute("insert into testclobs values (:1, :2)", - (i + 1, "String data " + longString + ' End of string')) -con.commit() - -print("Querying data...") -cur.execute("select * from testclobs where id = :id", {'id': 1}) -(id, clob) = cur.fetchone() -print("CLOB length:", clob.size()) -clobdata = clob.read() -print("CLOB data:", clobdata) diff --git a/samples/tutorial/clob_string.py b/samples/tutorial/clob_string.py deleted file mode 100644 index c95f39c5..00000000 --- a/samples/tutorial/clob_string.py +++ /dev/null @@ -1,35 +0,0 @@ -#------------------------------------------------------------------------------ -# clob_string.py (Section 7.2) -#------------------------------------------------------------------------------ - -#------------------------------------------------------------------------------ -# Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. -#------------------------------------------------------------------------------ - -import cx_Oracle -import db_config - -con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn) -cur = con.cursor() - -print("Inserting data...") -cur.execute("truncate table testclobs") -longString = "" -for i in range(5): - char = chr(ord('A') + i) - longString += char * 250 - cur.execute("insert into testclobs values (:1, :2)", - (i + 1, "String data " + longString + ' End of string')) -con.commit() - -def OutputTypeHandler(cursor, name, defaultType, size, precision, scale): - if defaultType == cx_Oracle.CLOB: - return cursor.var(cx_Oracle.LONG_STRING, arraysize = cursor.arraysize) - -con.outputtypehandler = OutputTypeHandler - -print("Querying data...") -cur.execute("select * from testclobs where id = :id", {'id': 1}) -(id, clobdata) = cur.fetchone() -print("CLOB length:", len(clobdata)) -print("CLOB data:", clobdata) diff --git a/samples/tutorial/connect.py b/samples/tutorial/connect.py deleted file mode 100644 index 948cd795..00000000 --- a/samples/tutorial/connect.py +++ /dev/null @@ -1,13 +0,0 @@ -#------------------------------------------------------------------------------ -# connect.py (Section 1.1 and 1.2) -#------------------------------------------------------------------------------ - -#------------------------------------------------------------------------------ -# Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. -#------------------------------------------------------------------------------ - -import cx_Oracle -import db_config - -con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn) -print("Database version:", con.version) diff --git a/samples/tutorial/connect_drcp.py b/samples/tutorial/connect_drcp.py deleted file mode 100644 index 61134db4..00000000 --- a/samples/tutorial/connect_drcp.py +++ /dev/null @@ -1,14 +0,0 @@ -#------------------------------------------------------------------------------ -# connect_drcp.py (Section 2.3 and 2.5) -#------------------------------------------------------------------------------ - -#------------------------------------------------------------------------------ -# Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. -#------------------------------------------------------------------------------ - -import cx_Oracle -import db_config - -con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn + ":pooled", - cclass="PYTHONHOL", purity=cx_Oracle.ATTR_PURITY_SELF) -print("Database version:", con.version) diff --git a/samples/tutorial/connect_pool.py b/samples/tutorial/connect_pool.py deleted file mode 100644 index 0e463747..00000000 --- a/samples/tutorial/connect_pool.py +++ /dev/null @@ -1,34 +0,0 @@ -#------------------------------------------------------------------------------ -# connect_pool.py (Section 2.1) -#------------------------------------------------------------------------------ - -#------------------------------------------------------------------------------ -# Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. -#------------------------------------------------------------------------------ - -import cx_Oracle -import threading -import db_config - -pool = cx_Oracle.SessionPool(db_config.user, db_config.pw, db_config.dsn, - min = 2, max = 5, increment = 1, threaded = True, - getmode = cx_Oracle.SPOOL_ATTRVAL_WAIT) - -def Query(): - con = pool.acquire() - cur = con.cursor() - for i in range(4): - cur.execute("select myseq.nextval from dual") - seqval, = cur.fetchone() - print("Thread", threading.current_thread().name, "fetched sequence =", seqval) - -thread1 = threading.Thread(name='#1', target=Query) -thread1.start() - -thread2 = threading.Thread(name='#2', target=Query) -thread2.start() - -thread1.join() -thread2.join() - -print("All done!") diff --git a/samples/tutorial/connect_pool2.py b/samples/tutorial/connect_pool2.py deleted file mode 100644 index e3199433..00000000 --- a/samples/tutorial/connect_pool2.py +++ /dev/null @@ -1,36 +0,0 @@ -#------------------------------------------------------------------------------ -# connect_pool2.py (Section 2.2 and 2.4) -#------------------------------------------------------------------------------ - -#------------------------------------------------------------------------------ -# Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. -#------------------------------------------------------------------------------ - -import cx_Oracle -import threading -import db_config - -pool = cx_Oracle.SessionPool(db_config.user, db_config.pw, db_config.dsn, - min = 2, max = 5, increment = 1, threaded = True, - getmode = cx_Oracle.SPOOL_ATTRVAL_WAIT) - -def Query(): - con = pool.acquire() - cur = con.cursor() - for i in range(4): - cur.execute("select myseq.nextval from dual") - seqval, = cur.fetchone() - print("Thread", threading.current_thread().name, "fetched sequence =", seqval) - -numberOfThreads = 2 -threadArray = [] - -for i in range(numberOfThreads): - thread = threading.Thread(name = '#' + str(i), target = Query) - threadArray.append(thread) - thread.start() - -for t in threadArray: - t.join() - -print("All done!") diff --git a/samples/tutorial/db_config.py b/samples/tutorial/db_config.py deleted file mode 100644 index a97a2087..00000000 --- a/samples/tutorial/db_config.py +++ /dev/null @@ -1,48 +0,0 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. -#------------------------------------------------------------------------------ - -import cx_Oracle -import sys -import os -import getpass - -###################################################################### - -# -# Oracle Client library configuration -# - -# On Linux this must be None. -# Instead, the Oracle environment must be set before Python starts. -instant_client_dir = None - -# On Windows, if your database is on the same machine, comment these lines out -# and let instant_client_dir be None. Otherwise, set this to your Instant -# Client directory. Note the use of the raw string r"..." so backslashes can -# be used as directory separators. -if sys.platform.startswith("win"): - instant_client_dir = r"c:\oracle\instantclient_19_10" - -# On macOS (Intel x86) set the directory to your Instant Client directory -if sys.platform.startswith("darwin"): - instant_client_dir = os.environ.get("HOME")+"/Downloads/instantclient_19_8" - -# This can be called at most once per process. -if instant_client_dir is not None: - cx_Oracle.init_oracle_client(lib_dir=instant_client_dir) - -###################################################################### - -# -# Tutorial credentials and connection string. -# Environment variable values are used, if they are defined. -# - -user = os.environ.get("PYTHON_USER", "pythonhol") - -dsn = os.environ.get("PYTHON_CONNECT_STRING", "localhost/orclpdb1") - -pw = os.environ.get("PYTHON_PASSWORD") -if pw is None: - pw = getpass.getpass("Enter password for %s: " % user) diff --git a/samples/tutorial/db_config.sql b/samples/tutorial/db_config.sql deleted file mode 100644 index e1a2096b..00000000 --- a/samples/tutorial/db_config.sql +++ /dev/null @@ -1,8 +0,0 @@ --- Default database username -def user = "pythonhol" - --- Default database connection string -def connect_string = "localhost/orclpdb1" - --- Prompt for the password -accept pw char prompt 'Enter database password for &user: ' hide diff --git a/samples/tutorial/drcp_query.sql b/samples/tutorial/drcp_query.sql deleted file mode 100644 index 528f56e6..00000000 --- a/samples/tutorial/drcp_query.sql +++ /dev/null @@ -1,22 +0,0 @@ -------------------------------------------------------------------------------- --- drcp_query.sql (Section 2.4 and 2.5) -------------------------------------------------------------------------------- - -/*----------------------------------------------------------------------------- - * Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. - *---------------------------------------------------------------------------*/ - -set echo off verify off feedback off linesize 80 pagesize 1000 - -accept pw char prompt 'Enter database password for SYSTEM: ' hide -accept connect_string char prompt 'Enter database connection string: ' - --- Connect to the CDB to see pool statistics -connect system/&pw@&connect_string - -col cclass_name format a40 - --- Some DRCP pool statistics -select cclass_name, num_requests, num_hits, num_misses from v$cpool_cc_stats; - -exit diff --git a/samples/tutorial/plsql_func.py b/samples/tutorial/plsql_func.py deleted file mode 100644 index 37dd1f85..00000000 --- a/samples/tutorial/plsql_func.py +++ /dev/null @@ -1,16 +0,0 @@ -#------------------------------------------------------------------------------ -# plsql_func.py (Section 5.1) -#------------------------------------------------------------------------------ - -#------------------------------------------------------------------------------ -# Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. -#------------------------------------------------------------------------------ - -import cx_Oracle -import db_config - -con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn) -cur = con.cursor() - -res = cur.callfunc('myfunc', int, ('abc', 2)) -print(res) diff --git a/samples/tutorial/plsql_func.sql b/samples/tutorial/plsql_func.sql deleted file mode 100644 index a5bcf443..00000000 --- a/samples/tutorial/plsql_func.sql +++ /dev/null @@ -1,35 +0,0 @@ -------------------------------------------------------------------------------- --- plsql_func.sql (Section 5.1) -------------------------------------------------------------------------------- - -/*----------------------------------------------------------------------------- - * Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. - *---------------------------------------------------------------------------*/ - -set echo off -@@db_config.sql -set echo on - -connect &user/&pw@&connect_string - -begin - execute immediate 'drop table ptab'; -exception -when others then - if sqlcode not in (-00942) then - raise; - end if; -end; -/ - -create table ptab (mydata varchar(20), myid number); - -create or replace function myfunc(d_p in varchar2, i_p in number) return number as - begin - insert into ptab (mydata, myid) values (d_p, i_p); - return (i_p * 2); - end; -/ -show errors - -exit diff --git a/samples/tutorial/plsql_proc.py b/samples/tutorial/plsql_proc.py deleted file mode 100644 index ef115b10..00000000 --- a/samples/tutorial/plsql_proc.py +++ /dev/null @@ -1,17 +0,0 @@ -#------------------------------------------------------------------------------ -# plsql_proc.py (Section 5.2) -#------------------------------------------------------------------------------ - -#------------------------------------------------------------------------------ -# Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. -#------------------------------------------------------------------------------ - -import cx_Oracle -import db_config - -con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn) -cur = con.cursor() - -myvar = cur.var(int) -cur.callproc('myproc', (123, myvar)) -print(myvar.getvalue()) diff --git a/samples/tutorial/plsql_proc.sql b/samples/tutorial/plsql_proc.sql deleted file mode 100644 index 2e0cb880..00000000 --- a/samples/tutorial/plsql_proc.sql +++ /dev/null @@ -1,22 +0,0 @@ -------------------------------------------------------------------------------- --- plsql_proc.sql (Section 5.2) -------------------------------------------------------------------------------- - -/*----------------------------------------------------------------------------- - * Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. - *---------------------------------------------------------------------------*/ - -set echo off -@@db_config.sql -set echo on - -connect &user/&pw@&connect_string - -create or replace procedure myproc(v1_p in number, v2_p out number) as -begin - v2_p := v1_p * 2; -end; -/ -show errors - -exit diff --git a/samples/tutorial/query.py b/samples/tutorial/query.py deleted file mode 100644 index 404857c2..00000000 --- a/samples/tutorial/query.py +++ /dev/null @@ -1,12 +0,0 @@ -#------------------------------------------------------------------------------ -# query.py (Section 1.3 and 1.4) -#------------------------------------------------------------------------------ - -#------------------------------------------------------------------------------ -# Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. -#------------------------------------------------------------------------------ - -import cx_Oracle -import db_config - -con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn) diff --git a/samples/tutorial/query2.py b/samples/tutorial/query2.py deleted file mode 100644 index ab5b1e3a..00000000 --- a/samples/tutorial/query2.py +++ /dev/null @@ -1,19 +0,0 @@ -#------------------------------------------------------------------------------ -# query2.py (Section 3.1) -#------------------------------------------------------------------------------ - -#------------------------------------------------------------------------------ -# Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. -#------------------------------------------------------------------------------ - -import cx_Oracle -import db_config - -con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn) - -cur = con.cursor() -cur.execute('select * from dept order by deptno') -for deptno, dname, loc in cur: - print("Department number: ", deptno) - print("Department name: ", dname) - print("Department location:", loc) diff --git a/samples/tutorial/query_arraysize.py b/samples/tutorial/query_arraysize.py deleted file mode 100644 index 39b177f1..00000000 --- a/samples/tutorial/query_arraysize.py +++ /dev/null @@ -1,25 +0,0 @@ -#------------------------------------------------------------------------------ -# query_arraysize.py (Section 3.5) -#------------------------------------------------------------------------------ - -#------------------------------------------------------------------------------ -# Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. -#------------------------------------------------------------------------------ - -import cx_Oracle -import time -import db_config - -con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn) - -start = time.time() - -cur = con.cursor() -cur.prefetchrows = 100 -cur.arraysize = 100 -cur.execute("select * from bigtab") -res = cur.fetchall() -# print(res) # uncomment to display the query results - -elapsed = (time.time() - start) -print(elapsed, "seconds") diff --git a/samples/tutorial/query_arraysize.sql b/samples/tutorial/query_arraysize.sql deleted file mode 100644 index 426c795c..00000000 --- a/samples/tutorial/query_arraysize.sql +++ /dev/null @@ -1,37 +0,0 @@ -------------------------------------------------------------------------------- --- query_arraysize.sql (Section 3.5) -------------------------------------------------------------------------------- - -/*----------------------------------------------------------------------------- - * Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. - *---------------------------------------------------------------------------*/ - -set echo off -@@db_config.sql -set echo on - -connect &user/&pw@&connect_string - -begin - execute immediate 'drop table bigtab'; -exception -when others then - if sqlcode not in (-00942) then - raise; - end if; -end; -/ - -create table bigtab (mycol varchar2(20)); -begin - for i in 1..20000 - loop - insert into bigtab (mycol) values (dbms_random.string('A',20)); - end loop; -end; -/ -show errors - -commit; - -exit diff --git a/samples/tutorial/query_many.py b/samples/tutorial/query_many.py deleted file mode 100644 index b0ef0a6e..00000000 --- a/samples/tutorial/query_many.py +++ /dev/null @@ -1,17 +0,0 @@ -#------------------------------------------------------------------------------ -# query_many.py (Section 3.3) -#------------------------------------------------------------------------------ - -#------------------------------------------------------------------------------ -# Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. -#------------------------------------------------------------------------------ - -import cx_Oracle -import db_config - -con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn) -cur = con.cursor() - -cur.execute("select * from dept order by deptno") -res = cur.fetchmany(numRows = 3) -print(res) diff --git a/samples/tutorial/query_one.py b/samples/tutorial/query_one.py deleted file mode 100644 index 3d7eaa98..00000000 --- a/samples/tutorial/query_one.py +++ /dev/null @@ -1,20 +0,0 @@ -#------------------------------------------------------------------------------ -# query_one.py (Section 3.2) -#------------------------------------------------------------------------------ - -#------------------------------------------------------------------------------ -# Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. -#------------------------------------------------------------------------------ - -import cx_Oracle -import db_config - -con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn) -cur = con.cursor() - -cur.execute("select * from dept order by deptno") -row = cur.fetchone() -print(row) - -row = cur.fetchone() -print(row) diff --git a/samples/tutorial/query_scroll.py b/samples/tutorial/query_scroll.py deleted file mode 100644 index 0b13142c..00000000 --- a/samples/tutorial/query_scroll.py +++ /dev/null @@ -1,21 +0,0 @@ -#------------------------------------------------------------------------------ -# query_scroll.py (Section 3.4) -#------------------------------------------------------------------------------ - -#------------------------------------------------------------------------------ -# Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. -#------------------------------------------------------------------------------ - -import cx_Oracle -import db_config - -con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn) -cur = con.cursor(scrollable = True) - -cur.execute("select * from dept order by deptno") - -cur.scroll(2, mode = "absolute") # go to second row -print(cur.fetchone()) - -cur.scroll(-1) # go back one row -print(cur.fetchone()) diff --git a/samples/tutorial/resources/base.css b/samples/tutorial/resources/base.css deleted file mode 100644 index f4ff3432..00000000 --- a/samples/tutorial/resources/base.css +++ /dev/null @@ -1,62 +0,0 @@ -body { - font-family: -apple-system, BlinkMacSystemFont, - "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", - "Segoe UI Emoji", "Segoe UI Symbol"; - font-size: 16px; - line-height: 1.5; - color: #333; - background-color: #fff; -} - -#container { - border: 2px solid #078311; - min-height: 103%; -} - -h1 { - background-color: #078311; - color: white; - font-size: 2.45em; - margin: 0; - padding: .3em; -} - -h2 { - color: #078311; - margin-bottom: .5em; -} - -hr { - color: #078311; -} - -#menu { - background-color: #00C029; - padding: .5em; - margin-bottom: .5em; -} - -#menu a { - text-decoration: none; - color: blue -} - -#menu a:hover { - text-decoration: underline -} - -#menu a:visited { - color: blue -} - -#content { - margin: 1em; -} - -.logo { - float: right; -} - -pre { - background: #E0E0E0; -} diff --git a/samples/tutorial/resources/community-py-200.png b/samples/tutorial/resources/community-py-200.png deleted file mode 100644 index b6192d0a..00000000 Binary files a/samples/tutorial/resources/community-py-200.png and /dev/null differ diff --git a/samples/tutorial/resources/cx_Oracle_arch.png b/samples/tutorial/resources/cx_Oracle_arch.png deleted file mode 100644 index f10e56da..00000000 Binary files a/samples/tutorial/resources/cx_Oracle_arch.png and /dev/null differ diff --git a/samples/tutorial/resources/favicon.ico b/samples/tutorial/resources/favicon.ico deleted file mode 100644 index da454326..00000000 Binary files a/samples/tutorial/resources/favicon.ico and /dev/null differ diff --git a/samples/tutorial/resources/python_nopool.png b/samples/tutorial/resources/python_nopool.png deleted file mode 100644 index a3947776..00000000 Binary files a/samples/tutorial/resources/python_nopool.png and /dev/null differ diff --git a/samples/tutorial/resources/python_pool.png b/samples/tutorial/resources/python_pool.png deleted file mode 100644 index 6eec28f4..00000000 Binary files a/samples/tutorial/resources/python_pool.png and /dev/null differ diff --git a/samples/tutorial/rowfactory.py b/samples/tutorial/rowfactory.py deleted file mode 100644 index 0b0dbd80..00000000 --- a/samples/tutorial/rowfactory.py +++ /dev/null @@ -1,25 +0,0 @@ -#------------------------------------------------------------------------------ -# rowfactory.py (Section 8.1) -#------------------------------------------------------------------------------ - -#------------------------------------------------------------------------------ -# Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. -#------------------------------------------------------------------------------ - -import collections -import cx_Oracle -import db_config - -con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn) -cur = con.cursor() - -cur.execute("select deptno, dname from dept") -res = cur.fetchall() - -print('Array indexes:') -for row in res: - print(row[0], "->", row[1]) - -print('Loop target variables:') -for c1, c2 in res: - print(c1, "->", c2) diff --git a/samples/tutorial/soda.py b/samples/tutorial/soda.py deleted file mode 100644 index 67473c50..00000000 --- a/samples/tutorial/soda.py +++ /dev/null @@ -1,47 +0,0 @@ -#------------------------------------------------------------------------------ -# soda.py (Section 11.1) -#------------------------------------------------------------------------------ - -#------------------------------------------------------------------------------ -# Copyright (c) 2019, 2021, Oracle and/or its affiliates. All rights reserved. -#------------------------------------------------------------------------------ - -import cx_Oracle -import db_config - -con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn) - -soda = con.getSodaDatabase() - -# Explicit metadata is used for maximum version portability -metadata = { - "keyColumn": { - "name":"ID" - }, - "contentColumn": { - "name": "JSON_DOCUMENT", - "sqlType": "BLOB" - }, - "versionColumn": { - "name": "VERSION", - "method": "UUID" - }, - "lastModifiedColumn": { - "name": "LAST_MODIFIED" - }, - "creationTimeColumn": { - "name": "CREATED_ON" - } - } - -collection = soda.createCollection("friends", metadata) - -content = {'name': 'Jared', 'age': 35, 'address': {'city': 'Melbourne'}} - -doc = collection.insertOneAndGet(content) -key = doc.key - -doc = collection.find().key(key).getOne() -content = doc.getContent() -print('Retrieved SODA document dictionary is:') -print(content) diff --git a/samples/tutorial/solutions/aq-dequeue.py b/samples/tutorial/solutions/aq-dequeue.py deleted file mode 100644 index 0b626244..00000000 --- a/samples/tutorial/solutions/aq-dequeue.py +++ /dev/null @@ -1,33 +0,0 @@ -#------------------------------------------------------------------------------ -# aq-dequeue.py (Section 10.1) -#------------------------------------------------------------------------------ - -#------------------------------------------------------------------------------ -# Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. -#------------------------------------------------------------------------------ - -import cx_Oracle -import decimal -import db_config - -con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn) -cur = con.cursor() - -BOOK_TYPE_NAME = "UDT_BOOK" -QUEUE_NAME = "BOOKS" -QUEUE_TABLE_NAME = "BOOK_QUEUE_TABLE" - -# Dequeue the messages -booksType = con.gettype(BOOK_TYPE_NAME) -queue = con.queue(QUEUE_NAME, booksType) -queue.deqOptions.wait = cx_Oracle.DEQ_NO_WAIT -queue.deqOptions.visibility = cx_Oracle.DEQ_IMMEDIATE - -print("\nDequeuing messages...") -while True: - props = queue.deqOne() - if not props: - break - print(props.payload.TITLE) - -print("\nDone.") diff --git a/samples/tutorial/solutions/aq-enqueue.py b/samples/tutorial/solutions/aq-enqueue.py deleted file mode 100644 index 767f1c04..00000000 --- a/samples/tutorial/solutions/aq-enqueue.py +++ /dev/null @@ -1,38 +0,0 @@ -#------------------------------------------------------------------------------ -# aq-enqueue.py (Section 10.1) -#------------------------------------------------------------------------------ - -#------------------------------------------------------------------------------ -# Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. -#------------------------------------------------------------------------------ - -import cx_Oracle -import decimal -import db_config - -con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn) -cur = con.cursor() - -BOOK_TYPE_NAME = "UDT_BOOK" -QUEUE_NAME = "BOOKS" -QUEUE_TABLE_NAME = "BOOK_QUEUE_TABLE" - -# Enqueue a few messages -print("Enqueuing messages...") - -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")) -] - -booksType = con.gettype(BOOK_TYPE_NAME) -queue = con.queue(QUEUE_NAME, booksType) - -for title, authors, price in BOOK_DATA: - book = booksType.newobject() - book.TITLE = title - book.AUTHORS = authors - book.PRICE = price - print(title) - queue.enqOne(con.msgproperties(payload=book, expiration=4)) - con.commit() diff --git a/samples/tutorial/solutions/aq-queuestart.py b/samples/tutorial/solutions/aq-queuestart.py deleted file mode 100644 index ceaaf10e..00000000 --- a/samples/tutorial/solutions/aq-queuestart.py +++ /dev/null @@ -1,47 +0,0 @@ -#------------------------------------------------------------------------------ -# aq-queuestart.py (Section 10.1) -#------------------------------------------------------------------------------ - -#------------------------------------------------------------------------------ -# Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. -#------------------------------------------------------------------------------ - -import cx_Oracle -import decimal -import db_config - -con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn) -cur = con.cursor() - -BOOK_TYPE_NAME = "UDT_BOOK" -QUEUE_NAME = "BOOKS" -QUEUE_TABLE_NAME = "BOOK_QUEUE_TABLE" - -# Cleanup -cur.execute( - """begin - dbms_aqadm.stop_queue('""" + QUEUE_NAME + """'); - dbms_aqadm.drop_queue('""" + QUEUE_NAME + """'); - dbms_aqadm.drop_queue_table('""" + QUEUE_TABLE_NAME + """'); - execute immediate 'drop type """ + BOOK_TYPE_NAME + """'; - exception when others then - if sqlcode <> -24010 then - raise; - end if; - end;""") - -# Create a type -print("Creating books type UDT_BOOK...") -cur.execute(""" - create type %s as object ( - title varchar2(100), - authors varchar2(100), - price number(5,2) - );""" % BOOK_TYPE_NAME) - -# Create queue table and queue and start the queue -print("Creating queue table...") -cur.callproc("dbms_aqadm.create_queue_table", - (QUEUE_TABLE_NAME, BOOK_TYPE_NAME)) -cur.callproc("dbms_aqadm.create_queue", (QUEUE_NAME, QUEUE_TABLE_NAME)) -cur.callproc("dbms_aqadm.start_queue", (QUEUE_NAME,)) diff --git a/samples/tutorial/solutions/bind_insert.py b/samples/tutorial/solutions/bind_insert.py deleted file mode 100644 index 9765759e..00000000 --- a/samples/tutorial/solutions/bind_insert.py +++ /dev/null @@ -1,31 +0,0 @@ -#------------------------------------------------------------------------------ -# bind_insert.py (Section 4.3) -#------------------------------------------------------------------------------ - -#------------------------------------------------------------------------------ -# Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. -#------------------------------------------------------------------------------ - -import cx_Oracle -import db_config - -con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn) -cur = con.cursor() - -rows = [ (1, "First" ), (2, "Second" ), - (3, "Third" ), (4, "Fourth" ), - (5, "Fifth" ), (6, "Sixth" ), - (6, "Duplicate" ), - (7, "Seventh" ) ] - -cur.executemany("insert into mytab(id, data) values (:1, :2)", rows, batcherrors = True) - -for error in cur.getbatcherrors(): - print("Error", error.message.rstrip(), "at row offset", error.offset) - -# Now query the results back - -cur2 = con.cursor() -cur2.execute('select * from mytab') -res = cur2.fetchall() -print(res) diff --git a/samples/tutorial/solutions/bind_sdo.py b/samples/tutorial/solutions/bind_sdo.py deleted file mode 100644 index 14f75277..00000000 --- a/samples/tutorial/solutions/bind_sdo.py +++ /dev/null @@ -1,79 +0,0 @@ -#------------------------------------------------------------------------------ -# bind_sdo.py (Section 4.4) -#------------------------------------------------------------------------------ - -#------------------------------------------------------------------------------ -# Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. -#------------------------------------------------------------------------------ - -import cx_Oracle -import db_config - -con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn) -cur = con.cursor() - -# Create table -cur.execute("""begin - execute immediate 'drop table testgeometry'; - exception when others then - if sqlcode <> -942 then - raise; - end if; - end;""") -cur.execute("""create table testgeometry ( - id number(9) not null, - geometry MDSYS.SDO_GEOMETRY not null)""") - -# Create and populate Oracle objects -typeObj = con.gettype("MDSYS.SDO_GEOMETRY") -elementInfoTypeObj = con.gettype("MDSYS.SDO_ELEM_INFO_ARRAY") -ordinateTypeObj = con.gettype("MDSYS.SDO_ORDINATE_ARRAY") -obj = typeObj.newobject() -obj.SDO_GTYPE = 2003 -obj.SDO_ELEM_INFO = elementInfoTypeObj.newobject() -obj.SDO_ELEM_INFO.extend([1, 1003, 3]) -obj.SDO_ORDINATES = ordinateTypeObj.newobject() -obj.SDO_ORDINATES.extend([1, 1, 5, 7]) - -pointTypeObj = con.gettype("MDSYS.SDO_POINT_TYPE") -obj.SDO_POINT = pointTypeObj.newobject() -obj.SDO_POINT.X = 1 -obj.SDO_POINT.Y = 2 -obj.SDO_POINT.Z = 3 - -print("Created object", obj) - -# Add a new row -print("Adding row to table...") -cur.execute("insert into testgeometry values (1, :objbv)", objbv = obj) -print("Row added!") - -# (Change below here) - -# Define a function to dump the contents of an Oracle object -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, "}") - -# Query the row -print("Querying row just inserted...") -cur.execute("select id, geometry from testgeometry") -for (id, obj) in cur: - print("Id: ", id) - dumpobject(obj) diff --git a/samples/tutorial/solutions/connect_pool2.py b/samples/tutorial/solutions/connect_pool2.py deleted file mode 100644 index 91898cb5..00000000 --- a/samples/tutorial/solutions/connect_pool2.py +++ /dev/null @@ -1,40 +0,0 @@ -#------------------------------------------------------------------------------ -# connect_pool2.py (Section 2.5) -#------------------------------------------------------------------------------ - -#------------------------------------------------------------------------------ -# Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. -#------------------------------------------------------------------------------ - -import cx_Oracle -import threading -import time -import db_config - -pool = cx_Oracle.SessionPool(db_config.user, db_config.pw, db_config.dsn + ":pooled", - min = 2, max = 5, increment = 1, threaded = True, - getmode = cx_Oracle.SPOOL_ATTRVAL_WAIT) - -def Query(): - con = pool.acquire(cclass="PYTHONHOL", purity=cx_Oracle.ATTR_PURITY_SELF) - #con = pool.acquire(cclass="PYTHONHOL", purity=cx_Oracle.ATTR_PURITY_NEW) - cur = con.cursor() - for i in range(4): - cur.execute("select myseq.nextval from dual") - seqval, = cur.fetchone() - print("Thread", threading.current_thread().name, "fetched sequence =", seqval) - #time.sleep(1) - -numberOfThreads = 5 -threadArray = [] - -for i in range(numberOfThreads): - thread = threading.Thread(name='#'+str(i), target=Query) - threadArray.append(thread) - #time.sleep(4) - thread.start() - -for t in threadArray: - t.join() - -print("All done!") diff --git a/samples/tutorial/solutions/db_config.py b/samples/tutorial/solutions/db_config.py deleted file mode 100644 index 375f6cc1..00000000 --- a/samples/tutorial/solutions/db_config.py +++ /dev/null @@ -1,4 +0,0 @@ -import os - -dirName = os.path.dirname(os.path.dirname(__file__)) -exec(open(os.path.join(dirName, "db_config.py"), "r").read()) diff --git a/samples/tutorial/solutions/query-2.py b/samples/tutorial/solutions/query-2.py deleted file mode 100644 index 931cba8b..00000000 --- a/samples/tutorial/solutions/query-2.py +++ /dev/null @@ -1,21 +0,0 @@ -#------------------------------------------------------------------------------ -# query.py (Section 1.4) -#------------------------------------------------------------------------------ - -#------------------------------------------------------------------------------ -# Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. -#------------------------------------------------------------------------------ - -import cx_Oracle -import db_config - -con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn) - -cur = con.cursor() -cur.execute("select * from dept order by deptno") -res = cur.fetchall() -for row in res: - print(row) - -cur.close() -con.close() diff --git a/samples/tutorial/solutions/query.py b/samples/tutorial/solutions/query.py deleted file mode 100644 index b1382b8f..00000000 --- a/samples/tutorial/solutions/query.py +++ /dev/null @@ -1,18 +0,0 @@ -#------------------------------------------------------------------------------ -# query.py (Section 1.3 and 1.4) -#------------------------------------------------------------------------------ - -#------------------------------------------------------------------------------ -# Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. -#------------------------------------------------------------------------------ - -import cx_Oracle -import db_config - -con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn) - -cur = con.cursor() -cur.execute("select * from dept order by deptno") -res = cur.fetchall() -for row in res: - print(row) diff --git a/samples/tutorial/solutions/query_many.py b/samples/tutorial/solutions/query_many.py deleted file mode 100644 index 0fea5f6f..00000000 --- a/samples/tutorial/solutions/query_many.py +++ /dev/null @@ -1,20 +0,0 @@ -#------------------------------------------------------------------------------ -# query_many.py (Section 3.3) -#------------------------------------------------------------------------------ - -#------------------------------------------------------------------------------ -# Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. -#------------------------------------------------------------------------------ - -import cx_Oracle -import db_config - -con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn) -cur = con.cursor() - -cur.execute("select * from dept order by deptno") -res = cur.fetchmany(numRows=3) -print(res) - -print(res[0]) # first row -print(res[0][1]) # second element of first row diff --git a/samples/tutorial/solutions/query_scroll.py b/samples/tutorial/solutions/query_scroll.py deleted file mode 100644 index 37b9f867..00000000 --- a/samples/tutorial/solutions/query_scroll.py +++ /dev/null @@ -1,27 +0,0 @@ -#------------------------------------------------------------------------------ -# query_scroll.py (Section 3.4) -#------------------------------------------------------------------------------ - -#------------------------------------------------------------------------------ -# Copyright 2017, 2021, Oracle and/or its affiliates. All rights reserved. -#------------------------------------------------------------------------------ - -import cx_Oracle -import db_config - -con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn) -cur = con.cursor(scrollable = True) - -cur.execute("select * from dept order by deptno") - -cur.scroll(2, mode = "absolute") # go to second row -print(cur.fetchone()) - -cur.scroll(-1) # go back one row -print(cur.fetchone()) - -cur.scroll(1) # go to next row -print(cur.fetchone()) - -cur.scroll(mode = "first") # go to first row -print(cur.fetchone()) diff --git a/samples/tutorial/solutions/rowfactory.py b/samples/tutorial/solutions/rowfactory.py deleted file mode 100644 index 17e425cc..00000000 --- a/samples/tutorial/solutions/rowfactory.py +++ /dev/null @@ -1,34 +0,0 @@ -#------------------------------------------------------------------------------ -# rowfactory.py (Section 8.1) -#------------------------------------------------------------------------------ - -#------------------------------------------------------------------------------ -# Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. -#------------------------------------------------------------------------------ - -import collections -import cx_Oracle -import db_config - -con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn) - -cur = con.cursor() - -cur.execute("select deptno, dname from dept") -res = cur.fetchall() - -print('Array indexes:') -for row in res: - print(row[0], "->", row[1]) - -print('Loop target variables:') -for c1, c2 in res: - print(c1, "->", c2) - -print('Rowfactory:') -cur.execute("select deptno, dname from dept") -cur.rowfactory = collections.namedtuple("MyClass", ["DeptNumber", "DeptName"]) - -res = cur.fetchall() -for row in res: - print(row.DeptNumber, "->", row.DeptName) diff --git a/samples/tutorial/solutions/soda.py b/samples/tutorial/solutions/soda.py deleted file mode 100644 index 1528aea1..00000000 --- a/samples/tutorial/solutions/soda.py +++ /dev/null @@ -1,68 +0,0 @@ -#------------------------------------------------------------------------------ -# soda.py (Section 11.2) -#------------------------------------------------------------------------------ - -#------------------------------------------------------------------------------ -# Copyright (c) 2019, 2021, Oracle and/or its affiliates. All rights reserved. -#------------------------------------------------------------------------------ - -import cx_Oracle -import db_config - -con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn) - -soda = con.getSodaDatabase() - -# Explicit metadata is used for maximum version portability -metadata = { - "keyColumn": { - "name":"ID" - }, - "contentColumn": { - "name": "JSON_DOCUMENT", - "sqlType": "BLOB" - }, - "versionColumn": { - "name": "VERSION", - "method": "UUID" - }, - "lastModifiedColumn": { - "name": "LAST_MODIFIED" - }, - "creationTimeColumn": { - "name": "CREATED_ON" - } - } - -collection = soda.createCollection("friends", metadata) - -content = {'name': 'Jared', 'age': 35, 'address': {'city': 'Melbourne'}} - -doc = collection.insertOneAndGet(content) -key = doc.key - -doc = collection.find().key(key).getOne() -content = doc.getContent() -print('Retrieved SODA document dictionary is:') -print(content) - -myDocs = [ - {'name': 'Gerald', 'age': 21, 'address': {'city': 'London'}}, - {'name': 'David', 'age': 28, 'address': {'city': 'Melbourne'}}, - {'name': 'Shawn', 'age': 20, 'address': {'city': 'San Francisco'}} -] -collection.insertMany(myDocs) - -filterSpec = { "address.city": "Melbourne" } -myDocuments = collection.find().filter(filterSpec).getDocuments() - -print('Melbourne people:') -for doc in myDocuments: - print(doc.getContent()["name"]) - -filterSpec = {'age': {'$lt': 25}} -myDocuments = collection.find().filter(filterSpec).getDocuments() - -print('Young people:') -for doc in myDocuments: - print(doc.getContent()["name"]) diff --git a/samples/tutorial/solutions/subclass.py b/samples/tutorial/solutions/subclass.py deleted file mode 100644 index 64877a8a..00000000 --- a/samples/tutorial/solutions/subclass.py +++ /dev/null @@ -1,39 +0,0 @@ -#------------------------------------------------------------------------------ -# subclass.py (Section 9.2) -#------------------------------------------------------------------------------ - -#------------------------------------------------------------------------------ -# Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. -#------------------------------------------------------------------------------ - -import cx_Oracle -import db_config - -class MyConnection(cx_Oracle.Connection): - - def __init__(self): - print("Connecting to database") - return super(MyConnection, self).__init__(db_config.user, db_config.pw, db_config.dsn) - - def cursor(self): - return MyCursor(self) - -class MyCursor(cx_Oracle.Cursor): - - def execute(self, statement, args): - print("Executing:", statement) - print("Arguments:") - for argIndex, arg in enumerate(args): - print(" Bind", argIndex + 1, "has value", repr(arg)) - return super(MyCursor, self).execute(statement, args) - - def fetchone(self): - print("Fetchone()") - return super(MyCursor, self).fetchone() - -con = MyConnection() -cur = con.cursor() - -cur.execute("select count(*) from emp where deptno = :bv", (10,)) -count, = cur.fetchone() -print("Number of rows:", count) diff --git a/samples/tutorial/solutions/type_converter.py b/samples/tutorial/solutions/type_converter.py deleted file mode 100644 index 2f4f4912..00000000 --- a/samples/tutorial/solutions/type_converter.py +++ /dev/null @@ -1,23 +0,0 @@ -#------------------------------------------------------------------------------ -# type_converter.py (Section 6.2) -#------------------------------------------------------------------------------ - -#------------------------------------------------------------------------------ -# Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. -#------------------------------------------------------------------------------ - -import cx_Oracle -import decimal -import db_config - -con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn) -cur = con.cursor() - -def ReturnNumbersAsDecimal(cursor, name, defaultType, size, precision, scale): - if defaultType == cx_Oracle.NUMBER: - return cursor.var(str, 9, cursor.arraysize, outconverter = decimal.Decimal) - -cur.outputtypehandler = ReturnNumbersAsDecimal - -for value, in cur.execute("select 0.1 from dual"): - print("Value:", value, "* 3 =", value * 3) diff --git a/samples/tutorial/solutions/type_output.py b/samples/tutorial/solutions/type_output.py deleted file mode 100644 index a1c31d4f..00000000 --- a/samples/tutorial/solutions/type_output.py +++ /dev/null @@ -1,28 +0,0 @@ -#------------------------------------------------------------------------------ -# type_output.py (Section 6.1) -#------------------------------------------------------------------------------ - -#------------------------------------------------------------------------------ -# Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. -#------------------------------------------------------------------------------ - -import cx_Oracle -import db_config - -con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn) - -cur = con.cursor() - -print("Standard output...") -for row in cur.execute("select * from dept"): - print(row) - -def ReturnNumbersAsStrings(cursor, name, defaultType, size, precision, scale): - if defaultType == cx_Oracle.NUMBER: - return cursor.var(str, 9, cursor.arraysize) - -print("Output type handler output...") -cur = con.cursor() -cur.outputtypehandler = ReturnNumbersAsStrings -for row in cur.execute("select * from dept"): - print(row) diff --git a/samples/tutorial/solutions/versions.py b/samples/tutorial/solutions/versions.py deleted file mode 100644 index 69246aad..00000000 --- a/samples/tutorial/solutions/versions.py +++ /dev/null @@ -1,16 +0,0 @@ -#------------------------------------------------------------------------------ -# versions.py (Section 1.5) -#------------------------------------------------------------------------------ - -#------------------------------------------------------------------------------ -# Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. -#------------------------------------------------------------------------------ - -import cx_Oracle -import db_config - -con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn) - -print(cx_Oracle.version) -print("Database version:", con.version) -print("Client version:", cx_Oracle.clientversion()) diff --git a/samples/tutorial/sql/create_user.sql b/samples/tutorial/sql/create_user.sql deleted file mode 100644 index c728772d..00000000 --- a/samples/tutorial/sql/create_user.sql +++ /dev/null @@ -1,59 +0,0 @@ -/*----------------------------------------------------------------------------- - * create_user.sql - * Creates a database user for the cx_Oracle tutorial - * - * Then run this like: - * - * sqlplus -l system/systempassword@localhost/orclpdb1 @create_user - * - * Substitute your actual password and connection string. - * For Oracle Autonmous Database, use 'admin' instead of system. - * You will be prompted for the new username and the new password to use. - * - * When you no longer need this user, run drop_user.sql to drop the user - * - *---------------------------------------------------------------------------*/ - -/*----------------------------------------------------------------------------- - * Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. - *---------------------------------------------------------------------------*/ - -whenever sqlerror exit failure -set verify off feedback off - -accept user char prompt 'Enter new database username: ' - -create user &user; - -grant - create session, - create table, - create procedure, - create type, - create sequence, - select any dictionary, - unlimited tablespace -to &user; - -begin - - for r in - ( select role - from dba_roles - where role in ('SODA_APP', 'AQ_ADMINISTRATOR_ROLE') - ) loop - execute immediate 'grant ' || r.role || ' to &user'; - end loop; - -end; -/ - -accept pw char prompt 'Enter password for &user: ' hide -alter user &user identified by "&pw"; - -prompt -prompt Database user &user has been created. -prompt Now you should run setup_tables.sql -prompt - -quit diff --git a/samples/tutorial/sql/drop_user.sql b/samples/tutorial/sql/drop_user.sql deleted file mode 100644 index 9d54ba31..00000000 --- a/samples/tutorial/sql/drop_user.sql +++ /dev/null @@ -1,52 +0,0 @@ -/*----------------------------------------------------------------------------- - * drop_user.sql - * Drops the database user used for the cx_Oracle tutorial - * - * Run this like: - * - * sqlplus -l system/systempassword@localhost/orclpdb1 @drop_user - * - * Substitute your actual password and connection string. - * For Oracle Autonmous Database, use 'admin' instead of system. - * You will be prompted for the user to drop. - * - *---------------------------------------------------------------------------*/ - -/*----------------------------------------------------------------------------- - * Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. - *---------------------------------------------------------------------------*/ - -whenever sqlerror exit failure -set verify off feedback off - -prompt WARNING: this scripts drops a user from the database -accept user char prompt 'Enter username to drop: ' - -begin - dbms_aqadm.stop_queue('BOOKS'); - dbms_aqadm.drop_queue('BOOKS'); - dbms_aqadm.drop_queue_table('BOOK_QUEUE_TABLE'); - exception when others then - if sqlcode <> -24010 then - raise; - end if; - end; -/ - -begin - - for r in - ( select username - from dba_users - where username in (upper('&user')) - ) loop - execute immediate 'drop user ' || r.username || ' cascade'; - end loop; -end; -/ - -prompt -prompt User &user has been dropped -prompt - -quit diff --git a/samples/tutorial/sql/setup_tables.sql b/samples/tutorial/sql/setup_tables.sql deleted file mode 100644 index c36b0cdd..00000000 --- a/samples/tutorial/sql/setup_tables.sql +++ /dev/null @@ -1,111 +0,0 @@ -/*----------------------------------------------------------------------------- - * setup_tables.sql - * Creates the tables etc used by the cx_Oracle tutorial. - * - * Run this like: - * - * sqlplus -l pythonhol/welcome@localhost/orclpdb1 @setup_tables - * - * Substitute your actual password and connection string. - * You may want to run create_user.sql before running this. - * - *---------------------------------------------------------------------------*/ - -/*----------------------------------------------------------------------------- - * Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. - *---------------------------------------------------------------------------*/ - -whenever sqlerror exit failure - --- EMP/DEPT tables - -begin execute immediate 'drop table emp'; exception when others then if sqlcode <> -942 then raise; end if; end; -/ - -create table emp - (empno number(4) not null, - ename varchar2(10), - job varchar2(9), - mgr number(4), - hiredate date, - sal number(7, 2), - comm number(7, 2), - deptno number(2)); - -insert into emp values - (7369, 'SMITH', 'CLERK', 7902, - to_date('17-DEC-1980', 'DD-MON-YYYY'), 800, NULL, 20); -insert into emp values - (7499, 'ALLEN', 'SALESMAN', 7698, - to_date('20-FEB-1981', 'DD-MON-YYYY'), 1600, 300, 30); -insert into emp values - (7521, 'WARD', 'SALESMAN', 7698, - to_date('22-FEB-1981', 'DD-MON-YYYY'), 1250, 500, 30); -insert into emp values - (7566, 'JONES', 'MANAGER', 7839, - to_date('2-APR-1981', 'DD-MON-YYYY'), 2975, NULL, 20); -insert into emp values - (7654, 'MARTIN', 'SALESMAN', 7698, - to_date('28-SEP-1981', 'DD-MON-YYYY'), 1250, 1400, 30); -insert into emp values - (7698, 'BLAKE', 'MANAGER', 7839, - to_date('1-MAY-1981', 'DD-MON-YYYY'), 2850, NULL, 30); -insert into emp values - (7782, 'CLARK', 'MANAGER', 7839, - to_date('9-JUN-1981', 'DD-MON-YYYY'), 2450, NULL, 10); -insert into emp values - (7788, 'SCOTT', 'ANALYST', 7566, - to_date('09-DEC-1982', 'DD-MON-YYYY'), 3000, NULL, 20); -insert into emp values - (7839, 'KING', 'PRESIDENT', NULL, - to_date('17-NOV-1981', 'DD-MON-YYYY'), 5000, NULL, 10); -insert into emp values - (7844, 'TURNER', 'SALESMAN', 7698, - to_date('8-SEP-1981', 'DD-MON-YYYY'), 1500, 0, 30); -insert into emp values - (7876, 'ADAMS', 'CLERK', 7788, - to_date('12-JAN-1983', 'DD-MON-YYYY'), 1100, NULL, 20); -insert into emp values - (7900, 'JAMES', 'CLERK', 7698, - to_date('3-DEC-1981', 'DD-MON-YYYY'), 950, NULL, 30); -insert into emp values - (7902, 'FORD', 'ANALYST', 7566, - to_date('3-DEC-1981', 'DD-MON-YYYY'), 3000, NULL, 20); -insert into emp values - (7934, 'MILLER', 'CLERK', 7782, - to_date('23-JAN-1982', 'DD-MON-YYYY'), 1300, NULL, 10); - -begin execute immediate 'drop table dept'; exception when others then if sqlcode <> -942 then raise; end if; end; -/ - -create table dept - (deptno number(2), - dname varchar2(14), - loc varchar2(13) ); - -insert into dept values (10, 'ACCOUNTING', 'NEW YORK'); -insert into dept values (20, 'RESEARCH', 'DALLAS'); -insert into dept values (30, 'SALES', 'CHICAGO'); -insert into dept values (40, 'OPERATIONS', 'BOSTON'); - -commit; - --- Table for clob.py and clob_string.py - -begin execute immediate 'drop table testclobs'; exception when others then if sqlcode <> -942 then raise; end if; end; -/ - -create table testclobs ( - id number not null, - myclob clob not null -); - --- Sequence for connect_pool.py - -begin execute immediate 'drop sequence myseq'; exception when others then if sqlcode <> -2289 then raise; end if; end; -/ - -create sequence myseq; - - -quit diff --git a/samples/tutorial/subclass.py b/samples/tutorial/subclass.py deleted file mode 100644 index 6f01475b..00000000 --- a/samples/tutorial/subclass.py +++ /dev/null @@ -1,23 +0,0 @@ -#------------------------------------------------------------------------------ -# subclass.py (Section 9.1 and 9.2) -#------------------------------------------------------------------------------ - -#------------------------------------------------------------------------------ -# Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. -#------------------------------------------------------------------------------ - -import cx_Oracle -import db_config - -class MyConnection(cx_Oracle.Connection): - - def __init__(self): - print("Connecting to database") - return super(MyConnection, self).__init__(db_config.user, db_config.pw, db_config.dsn) - -con = MyConnection() -cur = con.cursor() - -cur.execute("select count(*) from emp where deptno = :bv", (10,)) -count, = cur.fetchone() -print("Number of rows:", count) diff --git a/samples/tutorial/type_converter.py b/samples/tutorial/type_converter.py deleted file mode 100644 index 10102ff5..00000000 --- a/samples/tutorial/type_converter.py +++ /dev/null @@ -1,16 +0,0 @@ -#------------------------------------------------------------------------------ -# type_converter.py (Section 6.2) -#------------------------------------------------------------------------------ - -#------------------------------------------------------------------------------ -# Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. -#------------------------------------------------------------------------------ - -import cx_Oracle -import db_config - -con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn) -cur = con.cursor() - -for value, in cur.execute("select 0.1 from dual"): - print("Value:", value, "* 3 =", value * 3) diff --git a/samples/tutorial/type_input.py b/samples/tutorial/type_input.py deleted file mode 100644 index bfbcbbe1..00000000 --- a/samples/tutorial/type_input.py +++ /dev/null @@ -1,85 +0,0 @@ -#------------------------------------------------------------------------------ -# type_input.py (Section 6.3) -#------------------------------------------------------------------------------ - -#------------------------------------------------------------------------------ -# Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. -#------------------------------------------------------------------------------ - -import cx_Oracle -import db_config - -con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn) -cur = con.cursor() - -# Create table -cur.execute("""begin - execute immediate 'drop table testgeometry'; - exception when others then - if sqlcode <> -942 then - raise; - end if; - end;""") -cur.execute("""create table testgeometry ( - id number(9) not null, - geometry MDSYS.SDO_GEOMETRY not null)""") - -# Create a Python class for an SDO -class mySDO(object): - - def __init__(self, gtype, elemInfo, ordinates): - self.gtype = gtype - self.elemInfo = elemInfo - self.ordinates = ordinates - -# Get Oracle type information -objType = con.gettype("MDSYS.SDO_GEOMETRY") -elementInfoTypeObj = con.gettype("MDSYS.SDO_ELEM_INFO_ARRAY") -ordinateTypeObj = con.gettype("MDSYS.SDO_ORDINATE_ARRAY") - -# Convert a Python object to MDSYS.SDO_GEOMETRY -def SDOInConverter(value): - obj = objType.newobject() - obj.SDO_GTYPE = value.gtype - obj.SDO_ELEM_INFO = elementInfoTypeObj.newobject() - obj.SDO_ELEM_INFO.extend(value.elemInfo) - obj.SDO_ORDINATES = ordinateTypeObj.newobject() - obj.SDO_ORDINATES.extend(value.ordinates) - return obj - -def SDOInputTypeHandler(cursor, value, numElements): - if isinstance(value, mySDO): - return cursor.var(cx_Oracle.OBJECT, arraysize = numElements, - inconverter = SDOInConverter, typename = objType.name) - -sdo = mySDO(2003, [1, 1003, 3], [1, 1, 5, 7]) # Python object -cur.inputtypehandler = SDOInputTypeHandler -cur.execute("insert into testgeometry values (:1, :2)", (1, sdo)) - -# Define a function to dump the contents of an Oracle object -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, "}") - -# Query the row -print("Querying row just inserted...") -cur.execute("select id, geometry from testgeometry") -for (id, obj) in cur: - print("Id: ", id) - dumpobject(obj) diff --git a/samples/tutorial/type_output.py b/samples/tutorial/type_output.py deleted file mode 100644 index 4c1b4400..00000000 --- a/samples/tutorial/type_output.py +++ /dev/null @@ -1,17 +0,0 @@ -#------------------------------------------------------------------------------ -# type_output.py (Section 6.1) -#------------------------------------------------------------------------------ - -#------------------------------------------------------------------------------ -# Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. -#------------------------------------------------------------------------------ - -import cx_Oracle -import db_config - -con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn) -cur = con.cursor() - -print("Standard output...") -for row in cur.execute("select * from dept"): - print(row) diff --git a/samples/tutorial/versions.py b/samples/tutorial/versions.py deleted file mode 100644 index 5586438a..00000000 --- a/samples/tutorial/versions.py +++ /dev/null @@ -1,14 +0,0 @@ -#------------------------------------------------------------------------------ -# versions.py (Section 1.5) -#------------------------------------------------------------------------------ - -#------------------------------------------------------------------------------ -# Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. -#------------------------------------------------------------------------------ - -import cx_Oracle -import db_config - -con = cx_Oracle.connect(db_config.user, db_config.pw, db_config.dsn) - -print(cx_Oracle.version) diff --git a/samples/type_handlers.py b/samples/type_handlers.py deleted file mode 100644 index 7bdbe866..00000000 --- a/samples/type_handlers.py +++ /dev/null @@ -1,92 +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. -#------------------------------------------------------------------------------ - -#------------------------------------------------------------------------------ -# type_handlers.py -# This script demonstrates the use of input and output type handlers as well -# as variable input and output converters. These methods can be used to extend -# cx_Oracle in many ways. This script demonstrates the binding and querying of -# SQL objects as Python objects. -# -# This script requires cx_Oracle 5.0 and higher. -#------------------------------------------------------------------------------ - -import datetime - -import cx_Oracle as oracledb -import sample_env - -con = oracledb.connect(sample_env.get_main_connect_string()) -obj_type = con.gettype("UDT_BUILDING") - -class Building: - - def __init__(self, building_id, description, num_floors, date_built): - self.building_id = building_id - self.description = description - self.num_floors = num_floors - self.date_built = date_built - - def __repr__(self): - return "" % (self.building_id, self.description) - - -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 building_out_converter(obj): - return Building(int(obj.BUILDINGID), obj.DESCRIPTION, int(obj.NUMFLOORS), - obj.DATEBUILT) - - -def input_type_handler(cursor, value, num_elements): - if isinstance(value, Building): - return cursor.var(obj_type, arraysize=num_elements, - inconverter=building_in_converter) - -def output_type_handler(cursor, name, default_type, size, precision, scale): - if default_type == oracledb.OBJECT: - return cursor.var(obj_type, arraysize=cursor.arraysize, - outconverter=building_out_converter) - -buildings = [ - Building(1, "The First Building", 5, datetime.date(2007, 5, 18)), - Building(2, "The Second Building", 87, datetime.date(2010, 2, 7)), - Building(3, "The Third Building", 12, datetime.date(2005, 6, 19)), -] - -cur = con.cursor() -cur.inputtypehandler = input_type_handler -for building in buildings: - try: - cur.execute("insert into TestBuildings values (:1, :2)", - (building.building_id, building)) - except oracledb.DatabaseError as e: - error, = e.args - print("CONTEXT:", error.context) - print("MESSAGE:", error.message) - raise - -print("NO OUTPUT TYPE HANDLER:") -for row in cur.execute("select * from TestBuildings order by BuildingId"): - print(row) -print() - -cur = con.cursor() -cur.outputtypehandler = output_type_handler -print("WITH OUTPUT TYPE HANDLER:") -for row in cur.execute("select * from TestBuildings order by BuildingId"): - print(row) -print() diff --git a/samples/universal_rowids.py b/samples/universal_rowids.py deleted file mode 100644 index f8c5d0e3..00000000 --- a/samples/universal_rowids.py +++ /dev/null @@ -1,57 +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. -#------------------------------------------------------------------------------ - -#------------------------------------------------------------------------------ -# universal_rowids.py -# This script demonstrates the use of universal rowids. Universal rowids are -# used to identify rows in index organized tables. -# -# This script requires cx_Oracle 6.0 and higher. -#------------------------------------------------------------------------------ - -import datetime - -import cx_Oracle as oracledb -import sample_env - -DATA = [ - (1, "String #1", datetime.datetime(2017, 4, 4)), - (2, "String #2", datetime.datetime(2017, 4, 5)), - (3, "A" * 250, datetime.datetime(2017, 4, 6)) -] - -# truncate table so sample can be rerun -connection = oracledb.connect(sample_env.get_main_connect_string()) -cursor = connection.cursor() -print("Truncating table...") -cursor.execute("truncate table TestUniversalRowids") - -# populate table with a few rows -print("Populating table...") -for row in DATA: - print("Inserting", row) - cursor.execute("insert into TestUniversalRowids values (:1, :2, :3)", row) -connection.commit() - -# fetch the rowids from the table -rowids = [r for r, in cursor.execute("select rowid from TestUniversalRowids")] - -# fetch each of the rows given the rowid -for rowid in rowids: - print("-" * 79) - print("Rowid:", rowid) - cursor.execute(""" - select IntCol, StringCol, DateCol - from TestUniversalRowids - where rowid = :rid""", - rid = rowid) - int_col, string_col, dateCol = cursor.fetchone() - print("IntCol:", int_col) - print("StringCol:", string_col) - print("DateCol:", dateCol) diff --git a/test/test_3000_subscription.py b/test/test_3000_subscription.py index 595ebe09..daa5c523 100644 --- a/test/test_3000_subscription.py +++ b/test/test_3000_subscription.py @@ -7,39 +7,60 @@ """ import threading +import unittest import cx_Oracle as oracledb import test_env -class SubscriptionData(object): +class SubscriptionData: def __init__(self, num_messages_expected): self.condition = threading.Condition() self.num_messages_expected = num_messages_expected self.num_messages_received = 0 - self.table_operations = [] - self.row_operations = [] - self.rowids = [] - def CallbackHandler(self, message): + def _process_message(self, message): + pass + + def callback_handler(self, message): if message.type != oracledb.EVENT_DEREG: - table, = message.tables - self.table_operations.append(table.operation) - for row in table.rows: - self.row_operations.append(row.operation) - self.rowids.append(row.rowid) + self._process_message(message) self.num_messages_received += 1 if message.type == oracledb.EVENT_DEREG or \ self.num_messages_received == self.num_messages_expected: - self.condition.acquire() - self.condition.notify() - self.condition.release() + with self.condition: + self.condition.notify() + + def wait_for_messages(self): + if self.num_messages_received < self.num_messages_expected: + with self.condition: + self.condition.wait(10) + + +class AQSubscriptionData(SubscriptionData): + pass + + +class DMLSubscriptionData(SubscriptionData): + + def __init__(self, num_messages_expected): + super().__init__(num_messages_expected) + self.table_operations = [] + self.row_operations = [] + self.rowids = [] + + def _process_message(self, message): + table, = message.tables + self.table_operations.append(table.operation) + for row in table.rows: + self.row_operations.append(row.operation) + self.rowids.append(row.rowid) class TestCase(test_env.BaseTestCase): - def test_3000_subscription(self): - "3000 - test Subscription for insert, update, delete and truncate" + def test_3000_dml_subscription(self): + "3000 - test subscription for insert, update, delete and truncate" # skip if running on the Oracle Cloud, which does not support # subscriptions currently @@ -67,9 +88,9 @@ def test_3000_subscription(self): rowids = [] # set up subscription - data = SubscriptionData(5) + data = DMLSubscriptionData(5) connection = test_env.get_connection(threaded=True, events=True) - sub = connection.subscribe(callback=data.CallbackHandler, + sub = connection.subscribe(callback=data.callback_handler, timeout=10, qos=oracledb.SUBSCR_QOS_ROWIDS) sub.registerquery("select * from TestTempTable") connection.autocommit = True @@ -105,8 +126,7 @@ def test_3000_subscription(self): cursor.execute("truncate table TestTempTable") # wait for all messages to be sent - data.condition.acquire() - data.condition.wait(10) + data.wait_for_messages() # verify the correct messages were sent self.assertEqual(data.table_operations, table_operations) @@ -134,5 +154,30 @@ def test_3001_deprecations(self): self.assertRaises(oracledb.ProgrammingError, connection.subscribe, client_initiated=True, clientInitiated=True) + @unittest.skip("multiple subscriptions cannot be created simultaneously") + def test_3002_aq_subscription(self): + "3002 - test subscription for AQ" + + # create queue and clear it of all messages + queue = self.connection.queue("TEST_RAW_QUEUE") + queue.deqoptions.wait = oracledb.DEQ_NO_WAIT + while queue.deqone(): + pass + self.connection.commit() + + # set up subscription + data = AQSubscriptionData(1) + connection = test_env.get_connection(events=True) + sub = connection.subscribe(namespace=oracledb.SUBSCR_NAMESPACE_AQ, + name=queue.name, timeout=10, + callback=data.callback_handler) + + # enqueue a message + queue.enqone(self.connection.msgproperties(payload="Some data")) + self.connection.commit() + + # wait for all messages to be sent + data.wait_for_messages() + if __name__ == "__main__": test_env.run_test_cases()