Compare commits

...

271 Commits
7.2 ... main

Author SHA1 Message Date
Anthony Tuininga
70cbc43752 Further nudge users towards python-oracledb. 2025-03-26 20:19:34 -06:00
Anthony Tuininga
bed2c037f9 Relocate file per @LesiaChaban. 2024-07-30 17:09:37 -06:00
Anthony Tuininga
93d69d861b Make samples point to python-oracledb at runtime. 2024-05-31 16:47:41 -06:00
Anthony Tuininga
83774f9520 Update templates and READMEs to take note of python-oracledb. 2024-05-07 20:33:12 -06:00
Anthony Tuininga
6766bcaf27 Update ReadTheDocs configuration to avoid deprecation warnings with
ReadTheDocs.
2023-08-16 18:33:24 -06:00
Anthony Tuininga
a05b9a5233 Add more doc redirects. 2023-06-03 09:37:29 -06:00
Anthony Tuininga
5cfbb7d9e4 Remove semicolons in Python code examples (resolves #629). 2022-06-08 13:12:35 -06:00
Anthony Tuininga
fde577bf1f python-oracledb 1.0.0 has been released! 2022-05-25 14:43:26 -06:00
Anthony Tuininga
1ad43aa912 Doc improvements. 2022-05-25 14:41:35 -06:00
Anthony Tuininga
3db3e3772e Fix stale OCA link. 2022-05-25 14:40:44 -06:00
Anthony Tuininga
59c41535e3 Improved AQ test. 2022-05-25 14:37:50 -06:00
Anthony Tuininga
5728cf534e Try newer version of Sphinx. 2021-11-04 16:02:28 -06:00
Anthony Tuininga
3a40eeacaa ReadTheDocs only supports up to Python 3.8. 2021-11-04 15:58:51 -06:00
Anthony Tuininga
fde9ec78fc ReadTheDocs requires configuration so make it happy. 2021-11-04 15:57:32 -06:00
Anthony Tuininga
45118e0c31 Preparing to release cx_Oracle 8.3. 2021-11-04 13:20:41 -06:00
Anthony Tuininga
29ca919445 Add official support for Python 3.10. 2021-11-04 13:20:11 -06:00
Anthony Tuininga
ae687ce736 Improve documentation for WHERE IN clauses. 2021-11-04 13:19:26 -06:00
Anthony Tuininga
2d33fec37a Test suite improvements. 2021-11-04 13:19:04 -06:00
Anthony Tuininga
9db9d6907a Batch loading documentation improvements. 2021-11-04 13:16:44 -06:00
Anthony Tuininga
00dc44eade Update ODPI-C. 2021-11-04 11:30:34 -06:00
Anthony Tuininga
76157dd28a Add note that change to MessageProperties.msgid marked the attribute
read only.
2021-09-04 14:40:55 -06:00
Anthony Tuininga
83148ec574 Added test cases to verify that MessageProperties.msgid is calculated
correctly.
2021-09-03 13:57:20 -06:00
Anthony Tuininga
eb37d27464 Documentation improvements. 2021-09-03 11:00:47 -06:00
Anthony Tuininga
05a9097847 Add more tests. 2021-09-03 10:57:19 -06:00
Anthony Tuininga
438c885c20 Correct calculation of MessageProperties.msgid. 2021-09-03 10:56:24 -06:00
Anthony Tuininga
1347b04976 Update ODPI-C. 2021-09-02 11:16:16 -06:00
Anthony Tuininga
702a91be51 Update ODPI-C. 2021-09-01 11:12:51 -06:00
Anthony Tuininga
08117db459 Expand ADB documentation. 2021-07-26 13:32:18 -06:00
Anthony Tuininga
440163efe5 Improved samples and test suite. 2021-07-26 13:31:57 -06:00
Anthony Tuininga
c665d2efca Update formatting links. 2021-07-26 13:28:32 -06:00
Anthony Tuininga
dfeebc3358 Update ODPI-C. 2021-07-26 13:28:02 -06:00
Anthony Tuininga
5cce9efd49 Minor tweak to samples README. 2021-06-08 11:15:44 -06:00
Anthony Tuininga
cc067bf83e 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.
2021-06-08 11:15:13 -06:00
Anthony Tuininga
abb666706b Update ODPI-C. 2021-06-08 11:14:34 -06:00
Anthony Tuininga
4229a6d8ad cx_Oracle 8.2.1 is about to be released. 2021-06-01 13:02:07 -06:00
Anthony Tuininga
59558714ac Update SODA doc. 2021-06-01 12:03:03 -06:00
Anthony Tuininga
a61b14a5c6 Update ODPI-C. 2021-06-01 12:00:42 -06:00
Anthony Tuininga
5dccb9fb5f Use PEP 8 variable names. 2021-05-28 14:48:38 -06:00
Anthony Tuininga
206e85e4a2 Update ODPI-C. 2021-05-28 14:48:05 -06:00
Anthony Tuininga
df80067f47 Update ODPI-C. 2021-05-21 21:39:27 -06:00
Anthony Tuininga
d585cf06df Fixed crash when using the deprecated parameter name keywordParameters
with Cursor.callproc(); add test cases to cover this and other similar
situations.
2021-05-21 21:38:45 -06:00
Anthony Tuininga
ce4713c6f7 Fix typo. 2021-05-21 21:36:58 -06:00
Anthony Tuininga
31d94e7bf1 Bump version in preparation for new changes being made. 2021-05-21 21:36:30 -06:00
Anthony Tuininga
94e9489e25 Configuration requires master_doc (root_doc in Sphinx 4.0). 2021-05-18 17:04:49 -06:00
Anthony Tuininga
46a3d70ad8 Preparing to release cx_Oracle 8.2. 2021-05-18 16:59:00 -06:00
Anthony Tuininga
cd21e92edd Improve test suite. 2021-05-18 16:58:03 -06:00
Anthony Tuininga
0633e1017c Minor tweaks for PEP 8 compliance. 2021-05-18 16:56:55 -06:00
Anthony Tuininga
cfa2750854 Add support for pool reconfiguration. 2021-05-18 16:56:04 -06:00
Anthony Tuininga
d5461bd008 Further simplification. 2021-05-18 16:55:21 -06:00
Anthony Tuininga
07bb84e1b6 Remove duplicate forward declaration section. 2021-05-18 16:54:33 -06:00
Anthony Tuininga
4ed562c205 Documentation updates, including changes for PEP 8 compliance, to take
into account the fact that the default branch has changed to main, etc.
2021-05-18 16:53:31 -06:00
Anthony Tuininga
97be497fc9 Update ODPI-C to released 4.2.0. 2021-05-18 16:43:59 -06:00
Anthony Tuininga
c665e7a9e2 Update ODPI-C. 2021-05-13 15:58:14 -06:00
Anthony Tuininga
89eaea4f42 Update ODPI-C. 2021-05-13 14:38:47 -06:00
Anthony Tuininga
fbd9bfafb6 Update ODPI-C. 2021-05-07 11:14:57 -06:00
Anthony Tuininga
4661c6690f Move section. 2021-04-28 15:33:37 -06:00
Anthony Tuininga
4189983481 Adjust documentation to take into account recent changes regarding the
statement cache.
2021-04-28 15:31:04 -06:00
Anthony Tuininga
5680620f02 Add support for specifying the ping interval for pools. 2021-04-28 15:30:44 -06:00
Anthony Tuininga
932b413226 Adjust sample to match other AQ samples. 2021-04-28 15:29:54 -06:00
Anthony Tuininga
89aea21176 Support non-simple characters. 2021-04-23 16:12:12 -06:00
Anthony Tuininga
2c6468d992 Correct doc. 2021-04-23 16:11:38 -06:00
Anthony Tuininga
8c99d1ddde Final work on adjusting attribute, method and parameter names to be
consistent and to comply with PEP 8 naming guidelines.
2021-04-23 16:08:25 -06:00
Anthony Tuininga
96f938286d Further work on adjusting attribute, method and parameter names to be
consistent and to comply with PEP 8 naming guidelines; also adjust
implementation of #385 (originally done in pull request #549) to use the
parameter name `bypass_decode` instead of `bypassencoding`.
2021-04-23 16:05:42 -06:00
Anthony Tuininga
ab6e6f06ef Documentation and sample updates for clarity and to note the fact that
connection pools now always enable threading.
2021-04-23 14:21:45 -06:00
Anthony Tuininga
fa36066875 Added support for enabling the SODA metadata cache in Oracle Client
version 21.3 and higher (also available in Oracle Client 19 from 19.11).
This significantly improves the performance of
SodaDatabase.createCollection() and SodaDatabase.openCollection().
2021-04-23 14:18:46 -06:00
Anthony Tuininga
ba6e054a24 Further work on adjusting attribute, method and parameter names to be
consistent and to comply with PEP 8 naming guidelines.
2021-04-23 13:54:00 -06:00
Anthony Tuininga
82fb398b38 Doc tweaks. 2021-04-23 13:51:31 -06:00
Anthony Tuininga
fa8d6fa5be Migrate samples to use PEP 8 naming style consistently. 2021-04-23 13:50:06 -06:00
Anthony Tuininga
cccfa322c7 Simplify code to take advantage of feature added in Python 3.3. 2021-04-23 13:48:53 -06:00
Anthony Tuininga
0edb436abf Simplify samples. 2021-04-23 13:48:28 -06:00
Anthony Tuininga
9e8bc6676c Tweak doc. 2021-04-23 13:48:03 -06:00
Anthony Tuininga
7ad13e73d3 Added parameter stmtcachesize to cx_Oracle.connect() and
cx_Oracle.SessionPool() in order to facilitate specifying the initial
size of the statement cache.
2021-04-23 13:45:59 -06:00
Anthony Tuininga
cb1061a553 Remove extra word as noted in #550. 2021-04-23 13:42:26 -06:00
Anthony Tuininga
df89702a4e Stop passing unsupported flags to dpiSodaDocCursor_getNext(). 2021-04-23 13:41:48 -06:00
Anthony Tuininga
c527c06650 Further work on adjusting attribute, method and parameter names to be
consistent and to comply with PEP 8 naming guidelines; add a separate
section to the documentation to deal with all deprecations so that they
are all in one place.
2021-04-23 13:40:24 -06:00
Anthony Tuininga
4b72d0de02 Rename samples and test files in order to be consistent. 2021-04-23 13:36:00 -06:00
Anthony Tuininga
74d16d8962 Improved the test suite. 2021-04-23 12:04:29 -06:00
Anthony Tuininga
a1f8e1ffff URL updates. 2021-04-23 12:02:59 -06:00
Anthony Tuininga
5e2a363ac5 Modify the parameter names of the SessionPool constructor in order to
follow PEP 8 naming guidelines.
2021-04-23 12:01:52 -06:00
Anthony Tuininga
755b6fa861 Documentation tweaks. 2021-04-23 11:56:38 -06:00
Anthony Tuininga
4264d7bc5d Improved test suite; added check that the LOB type matches the expected
LOB type.
2021-04-23 11:53:13 -06:00
Anthony Tuininga
250b07299d Update CLA URL to new online system. 2021-04-23 11:47:15 -06:00
Anthony Tuininga
10f1c48289 Update ODPI-C. 2021-04-23 11:40:55 -06:00
Draco94
95baec2436
Implemented #385 enhancement and updated documentation (#549)
* Implemented #385 enhancement and updated documentation

Signed-off-by: Darko Djolovic <ddjolovic@outlook.com>

* Created flag to Cursor.var()

Signed-off-by: Darko Djolovic <ddjolovic@outlook.com>

* Removed first commit changes, updated documetnation

Signed-off-by: Darko Djolovic <ddjolovic@outlook.com>

* Added testing sample 'QueringRawData.py' and renamed attribute 'bypassstringencoding' to 'bypassencoding' with updated documentation

Signed-off-by: Darko Djolovic <ddjolovic@outlook.com>
2021-04-23 11:38:45 -06:00
Anthony Tuininga
ffa2086fd3 Clarify release note (#544). 2021-04-19 12:03:43 -06:00
Anthony Tuininga
c9110a33b8 Emphasize the Linux init_oracle_client() limitations. 2021-04-15 17:03:29 -06:00
Anthony Tuininga
517868eaf9 Added support for supplying hints to various SODA operations. 2021-04-15 16:59:34 -06:00
Anthony Tuininga
74b2e9a258 Update ODPI-C. 2021-04-15 16:58:38 -06:00
Anthony Tuininga
658f4cd3c2 Require an issue template. 2021-04-14 09:45:34 -06:00
Anthony Tuininga
8f901abce7 The distributed transaction handle assosciated with the connection is now
cleared on commit or rollback (`issue 530
<https://github.com/oracle/python-cx_Oracle/issues/530>`__).
2021-03-04 09:41:37 -07:00
Anthony Tuininga
bdf6b4ee4c Use explicit SODA metadata for maximum version interoperability. 2021-03-03 11:30:10 -07:00
Anthony Tuininga
1a42fb1de0 Update ODPI-C. 2021-03-03 11:24:07 -07:00
Anthony Tuininga
e1b05d5095 Correct file references. 2021-03-03 11:20:55 -07:00
Christopher Jones
fb2668e87c Update tutorial configuration and setup instructions 2021-03-03 17:14:32 +11:00
Christopher Jones
0a3aae5de8 Improve doc flow 2021-03-03 17:13:24 +11:00
Christopher Jones
5f60401c4a Use aq_administrator_role for AQ samples 2021-03-03 17:12:57 +11:00
Christopher Jones
bf381478c4 Update pool testcases 2021-03-03 17:11:55 +11:00
Christopher Jones
aa2e258c98 Use a better resolution architecture diagram 2021-03-03 17:11:15 +11:00
Anthony Tuininga
188608e748 Tweak documentation and README files. 2021-02-26 16:52:28 -07:00
Anthony Tuininga
0ff1003203 Eliminated memory leak when calling SodaOperation.filter() with a dictionary. 2021-01-21 19:53:29 -07:00
Anthony Tuininga
7382d035f4 Update ODPI-C and note SODA collection save() and saveAndGet() require Oracle
Client 19.9 or higher.
2021-01-21 10:30:34 -07:00
Anthony Tuininga
8ea16b1345 Tweak formatting to comply with PEP 8 and remove extraneous comments. 2021-01-21 10:29:43 -07:00
Anthony Tuininga
c92d251677 Bump version to 8.2 in preparation for further changes. 2021-01-21 10:29:05 -07:00
Anthony Tuininga
fa5fc20997 Fix typo. 2021-01-05 11:23:11 -07:00
Anthony Tuininga
9f32c9073e Remove trailing blank lines. 2021-01-05 11:21:50 -07:00
Anthony Tuininga
543c1b9d99 Minor tweaks to samples and documentation to take into account recent changes. 2021-01-05 11:20:29 -07:00
Anthony Tuininga
b199c5d57f Fix typos. 2020-12-21 15:01:01 -07:00
Anthony Tuininga
3b8c9cbd5f Added more test cases. 2020-12-21 14:57:45 -07:00
Anthony Tuininga
5c41ce1f05 Tweak top-level description for Oracle Client library and Python support
levels.
2020-12-08 13:18:07 -07:00
Anthony Tuininga
86c106dbf4 Preparing to release cx_Oracle 8.1. 2020-12-08 11:59:06 -07:00
Anthony Tuininga
d9cb6b1e56 Update templates. 2020-12-08 11:58:14 -07:00
Anthony Tuininga
f3984cee55 The connection created by using an external handle should never be used after
the external handle has been closed or destroyed (since otherwise the memory
used may have already been freed and reused by other parts of the application).
2020-12-08 11:51:54 -07:00
Anthony Tuininga
3a23957f6d Further tweaks to documentation and samples. 2020-12-08 11:47:53 -07:00
Anthony Tuininga
30979c6a57 Modify samples to follow the PEP 8 style guide. 2020-12-08 11:46:17 -07:00
Anthony Tuininga
6b9a7b011b Rename "base" to "test_env" to be clearer as to the purpose of the module. 2020-12-08 11:44:55 -07:00
Anthony Tuininga
c76492ec08 Documentation improvements. 2020-12-08 11:43:46 -07:00
Anthony Tuininga
9c9b92a39c Added new tests. 2020-12-08 11:42:53 -07:00
Anthony Tuininga
b59fe3b0be Added support for new JSON data type available in Oracle Client and Database 21
and higher.
2020-12-08 11:40:21 -07:00
Anthony Tuininga
9d4973c85d Update ODPI-C. 2020-12-08 11:00:56 -07:00
Anthony Tuininga
320fb1f31d Update stalebot configuration. 2020-11-18 21:26:10 -07:00
Anthony Tuininga
216bc42cf4 Eliminate use of deprecated function (which generates a warning in Python 3.9). 2020-11-12 14:28:17 -07:00
Anthony Tuininga
633d1141db Change to use PEP 8 format guidelines. 2020-11-12 14:27:23 -07:00
Anthony Tuininga
7e7133b09a The value of prefetchrows for REF CURSOR variables is now honored
(https://github.com/oracle/python-cx_Oracle/issues/482).
2020-11-12 14:24:42 -07:00
Anthony Tuininga
c8babe7209 Updated install instructions, mostly for macOS DMGs. 2020-11-12 14:23:30 -07:00
Anthony Tuininga
4dc91f5206 Update ODPI-C. 2020-11-12 14:21:33 -07:00
Anthony Tuininga
9cfe49f125 Enable https://github.com/marketplace/stale to close inactive issues. 2020-11-02 15:26:04 -07:00
Anthony Tuininga
229f9f5533 Rework test suite further to use PEP 8 style guidelines. 2020-11-02 15:25:51 -07:00
Anthony Tuininga
ef3d84318e The ability to pickle/unpickle Database and API types has been restored. 2020-11-02 15:24:23 -07:00
Anthony Tuininga
d242bc716d Rework build to use setuptools exclusively and enable use of pyproject.toml;
rework test suite to use tox and simplify test suite (also added a test number
to each test case for easier reference).
2020-11-02 15:21:52 -07:00
Anthony Tuininga
2194d81965 Added test cases for prefetch. 2020-11-02 15:20:45 -07:00
Anthony Tuininga
04fd1a7ad5 Add example showing how to create a stored proc and check for warnings. 2020-11-02 15:09:43 -07:00
Anthony Tuininga
551be9150e Update ODPI-C. 2020-11-02 15:06:18 -07:00
Anthony Tuininga
6ebd04ea5a Fix typo. 2020-10-07 14:23:01 -06:00
Anthony Tuininga
6ff7caba7c Add a security policy file
https://docs.github.com/en/github/managing-security-vulnerabilities/adding-a-security-policy-to-your-repository
2020-09-07 10:22:43 -06:00
Anthony Tuininga
0d28ea1e42 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.
2020-09-03 16:35:05 -06:00
Anthony Tuininga
a18cc34a4b Tweak release notes after release of cx_Oracle 8.0.1. 2020-09-03 15:06:06 -06:00
Anthony Tuininga
b4443c0f9f Documentation improvements. 2020-09-03 15:03:38 -06:00
Anthony Tuininga
a21e06a60c Update ODPI-C. 2020-09-03 14:58:11 -06:00
Anthony Tuininga
fa626f9001 Update ODPI-C. 2020-08-31 20:29:40 -06:00
Anthony Tuininga
73a5c56bd1 Preparing to release cx_Oracle 8.0.1. 2020-08-31 20:28:55 -06:00
Anthony Tuininga
769fbcf585 Add change to release notes. 2020-08-26 15:37:19 -06:00
Alex Henrie
f338af9d1c
Make numConnectDataArgs const (#472)
Signed-off-by: Alex Henrie <alexhenrie24@gmail.com>
2020-08-26 15:35:29 -06:00
Anthony Tuininga
d726c8be51 Add metadata specifying that Python 3.5 and higher is required (#456). 2020-07-30 09:54:14 -06:00
Anthony Tuininga
f3610cd2b9 Fix function header comments. 2020-07-28 09:52:47 -06:00
Anthony Tuininga
96f15f48da Move declaration of Python types, methods and members to the end of the file in
order to avoid unnecessary forward declarations.
2020-07-28 09:50:41 -06:00
Anthony Tuininga
dd492591a3 Remove version numbers. Fixes incorrect number. 2020-07-28 09:50:24 -06:00
Anthony Tuininga
b34f92e77f Improve pool documentation. 2020-07-28 09:49:55 -06:00
Anthony Tuininga
59231532a4 Add missing favicon. 2020-07-15 16:01:16 -06:00
Anthony Tuininga
bf6318da08 Add note about older versions. 2020-07-15 16:00:20 -06:00
Anthony Tuininga
7d017d89e5 Update GitHub issue templates. 2020-07-15 15:59:40 -06:00
Anthony Tuininga
ccda539172 dd a note about Python 2 support to the error message raised when an
unsupported version is detected.
2020-07-02 19:55:48 -06:00
Anthony Tuininga
b04f538467 Documentation improvements. 2020-07-02 19:54:47 -06:00
Anthony Tuininga
74a479087f Update ODPI-C to development version to include patch for issue #459; bump
version to 8.1 for further development.
2020-07-02 11:21:44 -06:00
Anthony Tuininga
10e5c258fe Update to ODPI-C 4.0.1 and add test to ensure that the offset is returned
correctly when a parse error is encountered.
2020-06-26 10:33:13 -06:00
Anthony Tuininga
8d719c3be4 Preparing to release cx_Oracle 8.0.0. 2020-06-25 15:25:30 -06:00
Anthony Tuininga
079927c8ce Various documentation and tutorial improvements. 2020-06-25 15:23:41 -06:00
Anthony Tuininga
3f06ea39a7 Update ODPI-C to the just released 4.0.0. 2020-06-25 15:18:57 -06:00
Anthony Tuininga
7df78b98d0 Tutorial improvements. 2020-06-19 10:42:06 -06:00
Anthony Tuininga
631b8729f5 Documentation improvements. 2020-06-19 10:41:51 -06:00
Anthony Tuininga
3a941687ea Added multiple consumer AQ example. 2020-06-19 10:39:19 -06:00
Anthony Tuininga
898b480035 Update ODPI-C. 2020-06-19 10:38:42 -06:00
Anthony Tuininga
8253bd2165 Bump reference to ODPI-C to version 4. 2020-06-12 16:00:16 -06:00
Anthony Tuininga
f8bcc6f0ec Added attribute cursor.prefetchrows to control the number of rows that the
Oracle Client library fetches into internal buffers when a query is
executed (https://github.com/oracle/python-cx_Oracle/issues/355).
2020-06-12 15:57:29 -06:00
Anthony Tuininga
0467db9e4b Tutorial updates to get ready for the coming weekend's Quest session. 2020-06-09 14:50:41 -06:00
Anthony Tuininga
5df9a73c5d Add cx_Oracle.init_oracle_client() for Oracle Client library initialization;
change default encoding to UTF-8.
2020-06-03 09:13:06 -06:00
Anthony Tuininga
c144216b95 Adjust test name to remove reference to very old version of Python. 2020-06-03 09:12:45 -06:00
Anthony Tuininga
4956aee0e5 Remove sample that is only relevant with Python 2. 2020-06-03 09:11:32 -06:00
Anthony Tuininga
c7a23115ad Remove remaining references to Python 2 syntax. 2020-06-03 09:11:03 -06:00
Anthony Tuininga
db1b7ae60d Various documentation improvements. 2020-06-03 09:10:38 -06:00
Anthony Tuininga
6e4fb6cf07 Update ODPI-C. 2020-06-03 09:09:51 -06:00
Anthony Tuininga
4646a18165 Remove unneeded code (and a deprecation warning with Python 3.9b1). 2020-05-19 11:42:46 -06:00
Anthony Tuininga
4b6b2c0dcc Return error if one occurs! 2020-05-18 16:38:44 -06:00
Anthony Tuininga
d5144aa58f Added test cases for cursor.lastrowid and SODA collection truncation (and added
code to check for situation where SODA support is lacking and stop running the
test suite for SODA in that case).
2020-05-18 16:34:58 -06:00
Anthony Tuininga
20686a1fc9 Various documentation and samples improvements. 2020-05-18 16:34:23 -06:00
Anthony Tuininga
633371db7d Small tweak to pull request for consistency in naming; update release notes
(https://github.com/oracle/python-cx_Oracle/pull/438).
2020-05-18 14:29:21 -06:00
Alex Henrie
5c6f55ef4d
Save string length instead of calling strlen in cxoCursor_var (#438)
Signed-off-by: Alex Henrie <alexhenrie24@gmail.com>
2020-05-18 14:26:35 -06:00
Anthony Tuininga
0b39b9e801 Update release notes. 2020-05-18 14:23:42 -06:00
Anthony Tuininga
f4148aeb6f Improve the doc on network pinging 2020-05-18 13:46:57 -06:00
Alex Henrie
ba141dd799
Create TestLongs and TestLongRaws tables without compression (#437)
Signed-off-by: Alex Henrie <alexhenrie24@gmail.com>
2020-05-18 13:43:13 -06:00
Anthony Tuininga
856d0aab76 Tweaks to boolean variable improvements patch supplied by Alex Henrie
(https://github.com/oracle/python-cx_Oracle/pull/435).
2020-05-15 21:47:40 -06:00
Alex Henrie
17fd92f8f6
Convert Python objects to booleans based on their Python truth value (#435)
Signed-off-by: Alex Henrie <alexhenrie24@gmail.com>
2020-05-15 17:00:13 -06:00
Anthony Tuininga
ba9a3ea3c2 Update ODPI-C. 2020-04-20 10:36:25 -06:00
Anthony Tuininga
d39ceb3280 Update release notes. 2020-04-20 10:25:44 -06:00
Alex Henrie
bd9d7759b2
Remove redundant decrement and return from cxoTransform_toPython (#423)
Signed-off-by: Alex Henrie <alexhenrie24@gmail.com>
2020-04-20 10:19:08 -06:00
Alex Henrie
af1281e535
Remove redundant assignment from cxoCursor_setBindVariables (#421)
Signed-off-by: Alex Henrie <alexhenrie24@gmail.com>
2020-04-18 21:39:39 -06:00
Alex Henrie
528dec6859
Fix memory leak on error path in cxoObjectType_initialize (#422)
Signed-off-by: Alex Henrie <alexhenrie24@gmail.com>
2020-04-18 21:38:31 -06:00
Alex Henrie
ca363bed8f
Use return value of snprintf instead of calling strlen unnecessarily (#420)
Signed-off-by: Alex Henrie <alexhenrie24@gmail.com>
2020-04-18 16:55:29 -06:00
Anthony Tuininga
fbe929d5c8 The database types CLOB, NCLOB, BFILE and BLOB no longer compare equal to
STRING and BINARY.
2020-04-17 10:19:44 -06:00
Alex Henrie
5ad2408a11
Consolidate string handling in cxoTransform_toPython (#419)
Signed-off-by: Alex Henrie <alexhenrie24@gmail.com>
2020-04-17 09:57:26 -06:00
Anthony Tuininga
6dc0263f1f Modify documentation based on change to comparison between database types and
DB API types.
2020-04-13 10:00:58 -06:00
Anthony Tuininga
c3561f3595 Eliminate treating BLOB and BFILE as BINARY and CLOB and NCLOB as STRING as
they return an object which does not behave the same way as strings and bytes
(https://github.com/oracle/python-cx_Oracle/issues/415).
2020-04-13 09:21:57 -06:00
Anthony Tuininga
54ab4b19f0 Improve DBMS_OUTPUT example, as suggested
(https://github.com/oracle/python-cx_Oracle/issues/412).
2020-04-06 14:03:20 -06:00
Anthony Tuininga
5071278938 And an examples section header and link to the tutorial. 2020-04-06 14:02:46 -06:00
Anthony Tuininga
c710fb89a6 Update tutorial links. 2020-04-06 14:02:20 -06:00
Anthony Tuininga
c513b71fe7 Use with clause when acquiring connections from a pool so that they are
returned automatically at the end of the block.
2020-04-06 13:53:26 -06:00
Anthony Tuininga
9377a9a0ef Documentation improvements. 2020-04-06 13:52:58 -06:00
Anthony Tuininga
3a4ee32022 Update ODPI-C. 2020-04-06 11:49:42 -06:00
Anthony Tuininga
dd31b41036 Added sample demonstrating the use of sharding capabilities. 2020-04-03 13:20:55 -06:00
Anthony Tuininga
8a301721fb Ensure that the new DbType objects are hashable
(https://github.com/oracle/python-cx_Oracle/issues/401).
2020-02-21 15:39:32 -07:00
Anthony Tuininga
52035c3b85 Add missing release note. 2020-02-14 10:33:43 -07:00
Anthony Tuininga
4ffbc9cac8 Added support for specifying the fetch array size when fetching documents from
a SODA collection -- available in Oracle Client 19.5 and higher.
2020-02-14 10:33:07 -07:00
Anthony Tuininga
9beb4aa907 Added support for SODA collection truncate, available in Oracle Client 20 and
higher.
2020-02-14 10:31:08 -07:00
Anthony Tuininga
44be257534 Add support for saving SODA documents into a collection, available in Oracle
Client 20 and higher.
2020-02-14 10:29:07 -07:00
Anthony Tuininga
b897106f00 Update ODPI-C. 2020-02-14 10:18:26 -07:00
Anthony Tuininga
f7b7785e82 Reworked type management to clarify and simplify code (see release notes for
details).
2020-02-10 10:02:03 -07:00
Anthony Tuininga
08346005a7 Use named field structure initialization in order to clarify code and reduce
clutter.
2020-02-10 09:57:30 -07:00
Anthony Tuininga
53a9f41ed1 Correct doc example (https://github.com/oracle/python-cx_Oracle/issues/390). 2020-01-27 09:36:07 -07:00
Anthony Tuininga
31f73b6ea1 Remove self-referencing link. 2020-01-27 09:35:49 -07:00
Anthony Tuininga
4874f68712 Add a second CQN example to demonstrate how to fetch modified rows inside the
notification callback.
2020-01-21 15:03:28 -07:00
Anthony Tuininga
4fd326bb63 Remove unnecessary imports. 2020-01-21 15:03:08 -07:00
Anthony Tuininga
f743d02857 Show how to reduce round trips with session callbacks. 2020-01-21 15:02:20 -07:00
Anthony Tuininga
8d18997d26 Update ODPI-C. 2020-01-21 15:01:59 -07:00
Anthony Tuininga
60fa25eaa1 Remove duplicate word. 2020-01-20 16:58:35 -07:00
Anthony Tuininga
e85a03b57b Remove commented out code 2020-01-06 20:14:11 -07:00
Anthony Tuininga
3666a4ab44 Change default Doc issue category to Enhancement 2020-01-06 20:13:45 -07:00
Anthony Tuininga
face13b7d4 Check the minimum supported Python version and raise an exception if an older
Python version is being used.
2019-12-04 15:20:59 -07:00
Anthony Tuininga
809ead0b9c Added support for starting up a database using a parameter file (PFILE), as
requested (https://github.com/oracle/python-cx_Oracle/issues/295).
2019-12-04 15:07:38 -07:00
Anthony Tuininga
6b73d2223f cx_Oracle 8 when it is released will not support Python 2.7. 2019-12-04 15:06:25 -07:00
Anthony Tuininga
cb1c3aaa73 Explicitly mention Python 3.8 support. 2019-12-02 17:05:17 -07:00
Anthony Tuininga
465024c12b Drop the development status now that cx_Oracle 7.3 is about to be released. 2019-12-02 16:59:23 -07:00
Anthony Tuininga
1ed26d8d86 Preparing to release cx_Oracle 7.3. 2019-12-02 16:37:13 -07:00
Anthony Tuininga
6ca6f2b0c3 Added a sample demonstrating the use of cursor.lastrowid. 2019-12-02 16:35:17 -07:00
Anthony Tuininga
22642e2826 Older versions of Python 2.7 don't have PyMem_Calloc() so use PyMem_Malloc()
and memset() instead for Python 2.7.
2019-11-29 15:20:42 -07:00
Anthony Tuininga
02d336c5e9 Tweak notes about supported versions. 2019-11-29 15:19:45 -07:00
Anthony Tuininga
79902e11e4 Added support for returning the rowid of the last row modified by an operation
on a cursor (or None if no row was modified).
2019-11-29 14:59:56 -07:00
Anthony Tuininga
5f70edd71c Be consistent and always raise cx_Oracle.InterfaceError when a connection is
closed and unusable.
2019-11-29 14:58:47 -07:00
Anthony Tuininga
428746f4de Update ODPI-C. 2019-11-29 14:56:24 -07:00
Anthony Tuininga
2b1bb5c115 Eliminate reference leak when message properties object is destroyed. 2019-11-25 21:43:45 -07:00
Anthony Tuininga
e6d1a981a0 Update ODPI-C. 2019-11-25 21:42:58 -07:00
Anthony Tuininga
7b144440aa Ensure that the session time zone is UTC so that values can be compared without
concern for the time zones of the client and database server.
2019-11-25 21:41:58 -07:00
Anthony Tuininga
0503265184 Add ORA-40479: internal JSON serializer error to the list of exceptions that
result in cx_Oracle.IntegrityError.
2019-11-25 21:41:32 -07:00
Anthony Tuininga
3f27185b9c Use new issue template format. 2019-11-20 14:36:38 -07:00
Anthony Tuininga
6f12ea7363 Eliminate reference leak when splitting the password and dsn components out of
a full connect string.
2019-11-20 14:34:11 -07:00
Anthony Tuininga
c447368230 Eliminate reference leak and ensure that memory is properly initialized in case
of error when using sharding keys.
2019-11-20 14:33:37 -07:00
Anthony Tuininga
efc3160d5f Update IC links following redirection. 2019-11-20 14:33:03 -07:00
Anthony Tuininga
07498fefa1 Various documentation improvements. 2019-11-20 14:31:58 -07:00
Anthony Tuininga
19ff7883be Update ODPI-C. 2019-11-20 14:27:07 -07:00
Anthony Tuininga
3b306187b0 Added support for client initiated connections for subscriptions. 2019-11-13 16:51:23 -07:00
Anthony Tuininga
dad0419003 Documentation improvements and a new section on connecting to Autonomous
Database.
2019-11-13 16:48:47 -07:00
Anthony Tuininga
089639c0f9 A value of T_BOOL must be represented as char and should be treated as such
consistently in order to avoid issues on big-endian architectures.
2019-11-13 16:48:20 -07:00
Anthony Tuininga
80b9a31fa6 Remove invalid test. 2019-11-13 16:48:06 -07:00
Anthony Tuininga
fcbf9ff17c DatabaseError, not ProgrammingError is now returned as this check is performed
at the ODPI-C level.
2019-11-13 16:47:45 -07:00
Anthony Tuininga
495541ca20 Add support for setting maxSessionsPerShard attribute for session pools. 2019-11-13 16:47:09 -07:00
Anthony Tuininga
9cb9842696 Eliminate unnecessary check (ODPI-C performs this check already). 2019-11-13 16:45:55 -07:00
Anthony Tuininga
a7b958d68a Added test cases for pls_integer and binary_integer data types. 2019-11-13 16:44:50 -07:00
Anthony Tuininga
cd9c7e3fa3 Adjust test suite so that it runs unchanged against Oracle Cloud databases:
- administrative user is ADMIN on the Oracle Cloud databases and SYSTEM on
  local databases (SYSDBA is not available on the Oracle Cloud database)
- environment variables CX_ORACLE_TEST_SYSDBA_USER and
  CX_ORACLE_TEST_SYSDBA_PASSWORD are replaced with CX_ORACLE_TEST_ADMIN_USER
  and CX_ORACLE_TEST_ADMIN_PASSWORD
- skip tests that change passwords as passwords on Oracle Cloud database are
  strictly controlled
- skip tests that check subscriptions as these are not currently supported on
  Oracle Cloud database
2019-11-13 16:43:49 -07:00
Anthony Tuininga
952580a565 Adjust samples so that they run unchanged against Oracle Cloud databases:
- administrative user is ADMIN on the Oracle Cloud databases and SYSTEM on
  local databases (SYSDBA is not available on the Oracle Cloud database)
- use dbms_session.sleep() instead of dbms_lock.sleep() where possible
- environment variables CX_ORACLE_SAMPLES_SYSDBA_USER and
  CX_ORACLE_SAMPLES_SYSDBA_PASSWORD are replaced with
  CX_ORACLE_SAMPLES_ADMIN_USER and CX_ORACLE_SAMPLES_ADMIN_PASSWORD
- new environment variable CX_ORACLE_SAMPLES_DRCP_CONNECT_STRING used for
  specifying the connect string to use for DRCP usage
2019-11-13 16:43:07 -07:00
Anthony Tuininga
d16c939f74 Adjust sample to take into account changes made to AQ support. 2019-11-13 16:41:10 -07:00
Anthony Tuininga
13204317d3 Update ODPI-C. 2019-11-13 16:40:23 -07:00
Anthony Tuininga
9b7137fb20 Update ODPI-C and simplify call to get server version. 2019-10-11 15:53:20 -06:00
Anthony Tuininga
e433c720c9 Improved documentation. 2019-10-01 11:17:29 -06:00
Anthony Tuininga
ee893d35f4 Releasing cx_Oracle 7.2.3. 2019-10-01 11:17:08 -06:00
Anthony Tuininga
2d0b4895aa Update ODPI-C. 2019-09-28 15:09:46 -06:00
Christopher Jones
7becddbe9c Update doc links 2019-09-18 23:45:35 +10:00
Anthony Tuininga
942f4aa015 Update ODPI-C. 2019-08-30 10:57:50 -06:00
Anthony Tuininga
4e783a9e67 Improve documentation based on feedback
(https://github.com/oracle/python-cx_Oracle/issues/343).
2019-08-28 10:40:51 -06:00
Anthony Tuininga
a16a944964 Restore support for setting numeric bind variables with boolean values. 2019-08-22 21:39:15 -06:00
Anthony Tuininga
0ecf3e7187 Releasing cx_Oracle 7.2.2. 2019-08-12 21:22:26 -06:00
Anthony Tuininga
06a336926a Bump version back to 7.3 in preparation for additional changes. 2019-08-08 14:23:41 -06:00
Anthony Tuininga
2cec047870 Update to new default PDB service name. 2019-08-08 14:19:23 -06:00
Anthony Tuininga
2c9f8d5ca4 Added user guide. 2019-08-08 14:15:14 -06:00
Anthony Tuininga
3696b35254 Bump version back to 7.2.1 in order to release user guide. 2019-08-08 14:13:51 -06:00
Anthony Tuininga
982a21b891 Clarify that cursor.arrayvar() can only be used for PL/SQL associative arrays
with contiguous keys.
2019-07-26 09:41:58 -06:00
Anthony Tuininga
e87c09a46d Bumped version. 2019-07-25 14:34:43 -06:00
Anthony Tuininga
65c22693a6 Releasing cx_Oracle 7.2.1. 2019-07-25 14:29:33 -06:00
Anthony Tuininga
f91f907adf Correct spelling mistakes and formatting. 2019-07-22 16:55:15 -06:00
Anthony Tuininga
d526de1893 Resolve MemoryError exception on Windows when using an output type handler
(https://github.com/oracle/python-cx_Oracle/issues/330).
2019-07-15 14:23:48 -06:00
Anthony Tuininga
ff25784c21 Use most recent default connect string in tutorial. 2019-07-15 14:23:05 -06:00
Anthony Tuininga
b104d40016 Added a SODA section to the tutorial. 2019-07-15 14:22:28 -06:00
Anthony Tuininga
e37e502724 Added test cases, particularly for bulk AQ operations. 2019-07-05 14:32:59 -06:00
Anthony Tuininga
6bdc8ee465 Improve documentation, particularly regarding proxy usage. 2019-07-05 14:32:12 -06:00
277 changed files with 25916 additions and 12770 deletions

10
.github/ISSUE_TEMPLATE/announcements.md vendored Normal file
View File

@ -0,0 +1,10 @@
---
name: Announcements
about: Use this if you are sharing something interesting
title: ''
labels: announcement
assignees: ''
---
<!-- Let us know if you are speaking at a conference on cx_Oracle, or have a new package or app that uses cx_Oracle, or something similarly exciting. -->

80
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View File

@ -0,0 +1,80 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: bug
assignees: ''
---
<!--
Thank you for using cx_Oracle.
See https://www.oracle.com/corporate/security-practices/assurance/vulnerability/reporting.html for how to report security issues
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
-->
1. What versions are you using?
<!--
Give your database version.
Also run Python and show the output of:
import sys
import platform
print("platform.platform:", platform.platform())
print("sys.maxsize > 2**32:", sys.maxsize > 2**32)
print("platform.python_version:", platform.python_version())
And:
import cx_Oracle
print("cx_Oracle.version:", cx_Oracle.version)
print("cx_Oracle.clientversion:", cx_Oracle.clientversion())
-->
2. Is it an error or a hang or a crash?
3. What error(s) or behavior you are seeing?
<!--
Cut and paste text showing the command you ran. No screenshots.
Use a gist for long screen output and logs: see https://gist.github.com/
-->
4. Include a runnable Python script that shows the problem.
<!--
Include all SQL needed to create the database schema.
Format code by using three backticks on a line before and after code snippets, for example:
```
import cx_Oracle
```
-->

1
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View File

@ -0,0 +1 @@
blank_issues_enabled: false

View File

@ -0,0 +1,21 @@
---
name: Documentation and Example Improvements
about: Use this to suggest changes to documentation and examples
title: ''
labels: enhancement
assignees: ''
---
<!--
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
Please submit your documentation and example improvements to the python-oracledb repository.
No further releases under the cx_Oracle namespace are planned.
-->

View File

@ -0,0 +1,21 @@
---
name: Enhancement Requests
about: Use this for enhancement requests
title: ''
labels: enhancement
assignees: ''
---
<!--
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
Please submit your enhancement requests to the python-oracledb repository.
No further releases under the cx_Oracle namespace are planned.
-->

View File

@ -0,0 +1,72 @@
---
name: Questions and Runtime Problems
about: For general cx_Oracle questions
title: ''
labels: question
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
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.
Use Markdown syntax, see https://docs.github.com/github/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax
GitHub issues that are not updated for a month may be automatically closed. Feel free to update them at any time.
-->
1. What versions are you using?
<!--
Give your database version.
Also run Python and show the output of:
import sys
import platform
print("platform.platform:", platform.platform())
print("sys.maxsize > 2**32:", sys.maxsize > 2**32)
print("platform.python_version:", platform.python_version())
And:
import cx_Oracle
print("cx_Oracle.version:", cx_Oracle.version)
print("cx_Oracle.clientversion:", cx_Oracle.clientversion())
-->
2. Describe the problem
<!-- Cut and paste text showing the command you ran. No screenshots. -->
3. Include a runnable Python script that shows the problem.
<!--
Include all SQL needed to create the database schema.
Use a gist for long code: see https://gist.github.com/
Format code by using three backticks on a line before and after code snippets, for example:
```
import cx_Oracle
```
-->

View File

@ -0,0 +1,64 @@
---
name: Installation Problems
about: Use this for cx_Oracle installation questions
title: ''
labels: install & configuration
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:
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
Review the user manual: https://cx-oracle.readthedocs.io/en/latest/index.html
If you have a `DPI-1047`, `DPI-1050` or `DPI-1072` error, re-review the links above.
Google any errors.
Then please answer these questions so we can help you.
GitHub issues that are not updated for a month may be automatically closed. Feel free to update them at any time.
-->
1. What versions are you using?
<!--
Give your database version.
Also run Python and show the output of:
import sys
import platform
print("platform.platform:", platform.platform())
print("sys.maxsize > 2**32:", sys.maxsize > 2**32)
print("platform.python_version:", platform.python_version())
-->
2. Describe the problem
<!-- Cut and paste text showing the command you ran. No screenshots. -->
3. Show the directory listing where your Oracle Client libraries are installed (e.g. the Instant Client directory). Is it 64-bit or 32-bit?
4. Show what the `PATH` environment variable (on Windows) or `LD_LIBRARY_PATH` (on Linux) is set to?
5. Show any Oracle environment variables set (e.g. ORACLE_HOME, ORACLE_BASE).

6
.github/SUPPORT.md vendored
View File

@ -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

View File

@ -1,45 +0,0 @@
*Delete unnecessary parts of this template.*
### For security issues:
See https://www.oracle.com/support/assurance/vulnerability-remediation/reporting-security-vulnerabilities.html for how to report security issues.
### For general questions:
Describe exactly what you did and what you want to happen.
Use the questions at the bottom of this template as a guide.
Use Markdown syntax, particularly for code blocks: see https://help.github.com/articles/basic-writing-and-formatting-syntax/#quoting-code
### For installation issues:
Use a gist for screen output and logs: see https://gist.github.com/
**Do not paste long output into this issue**
Review your output and logs. **Google any errors**
Try to install in a different way. Try some potential solutions.
Review the install instructions at
http://cx-oracle.readthedocs.io/en/latest/installation.html
#### Answer the following questions:
1. What is your version of Python? Is it 32-bit or 64-bit?
2. What is your cx_Oracle version?
3. What *exact* command caused the problem (e.g. what command did you try to install with)? Who were you logged in as?
4. What error(s) you are seeing?
5. What OS (and version) is Python executing on?
6. What is your version of the Oracle client (e.g. Instant Client)? How was it installed? Where is it installed?
7. What is your Oracle Database version?
8. What is the `PATH` environment variable (on Windows) or `LD_LIBRARY_PATH` (on Linux) set to? On macOS, what is in `~/lib`?
9. What Oracle environment variables did you set? How *exactly* did you set them?
10. Do you have a small, single Python script that immediately runs to show us the problem?

View File

@ -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 <you@example.org>
```
This can be automatically added to pull requests by committing with:
```
git commit --signoff
````
No further releases under the cx_Oracle namespace are planned.

23
.github/stale.yml vendored Normal file
View File

@ -0,0 +1,23 @@
# https://probot.github.io/apps/stale/
# Number of days of inactivity before an issue becomes stale
daysUntilStale: 30
# Number of days of inactivity before a stale issue is closed
daysUntilClose: 7
# Issues with these labels will never be considered stale
exemptLabels:
- pinned
- enhancement
- bug
- announcement
- OCA accepted
# Label to use when marking an issue as stale
staleLabel: inactive
# Comment to post when marking an issue as stale. Set to `false` to disable
markComment: >
This issue has been automatically marked as inactive because it has not been
updated recently. It will be closed if no further activity occurs. Thank you
for your contributions.
# Comment to post when closing a stale issue. Set to `false` to disable
closeComment: >
This issue has been automatically closed because it has not been updated for a month.

1
.gitignore vendored
View File

@ -1,4 +1,5 @@
*.pyc
.tox/
build/
dist/
doc/build

16
.readthedocs.yaml Normal file
View File

@ -0,0 +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
# declare Python requirements required to build docs
python:
install:
- requirements: doc/requirements.txt

View File

@ -1,24 +1,8 @@
# Contributing to cx_Oracle
# Contributing
*Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.*
The cx_Oracle driver was renamed to python-oracledb in May 2022. It has a new
repository at https://github.com/oracle/python-oracledb
Pull requests can be made under
[The Oracle Contributor Agreement](https://www.oracle.com/technetwork/community/oca-486395.html)
(OCA).
Please submit your contributions to the python-oracledb repository.
For pull requests to be accepted into cx_Oracle, the bottom of
your commit message must have the following line using your name and
e-mail address as it appears in the OCA Signatories list.
```
Signed-off-by: Your Name <you@example.org>
```
This can be automatically added to pull requests by committing with:
```
git commit --signoff
````
Only pull requests from committers that can be verified as having
signed the OCA can be accepted.
No further releases under the cx_Oracle namespace are planned.

View File

@ -1,59 +1,33 @@
# cx_Oracle version 7.2
# 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 7 has been tested with Python version 2.7, and with versions
3.5 and higher. You can use cx_Oracle with Oracle 11.2 and higher client
libraries (including Oracle 18.3). Oracle's standard client-server version
interoperability allows connection to both older and newer databases,
for example Oracle 18.3 client libraries can connect to Oracle
Database 11.2.
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/master/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/master/LICENSE.txt
[5]: http://lists.sourceforge.net/lists/listinfo/cx-oracle-users
[6]: https://github.com/oracle/python-cx_Oracle/tree/master/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/master/test
[12]: https://github.com/oracle/python-cx_Oracle/tree/master/samples
[14]: http://cx-oracle.readthedocs.io/en/latest/releasenotes.html
[15]: http://cx-oracle.readthedocs.io/en/latest/installation.html
The source code for python-oracledb is at
[github.com/oracle/python-oracledb](https://github.com/oracle/python-oracledb).

View File

@ -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.

37
SECURITY.md Normal file
View File

@ -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/

2
doc/requirements.txt Normal file
View File

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

View File

@ -1,8 +1,17 @@
.. _aq:
****************
Advanced Queuing
****************
*********************
Advanced Queuing (AQ)
*********************
.. note::
**cx_Oracle has a major new release under a new name and homepage**
`python-oracledb <https://oracle.github.io/python-oracledb/>`__.
**New projects should install python-oracledb instead of cx_Oracle.**
See :ref:`aqusermanual` for more information about using AQ in cx_Oracle.
.. note::
@ -23,33 +32,42 @@ used to enqueue and dequeue messages.
which the queue was created.
.. method:: Queue.deqMany(maxMessages)
.. method:: Queue.deqmany(maxMessages)
Dequeues up to the specified number of messages from the queue and returns
a list of these messages. Each element of the returned list is a
:ref:`message property<msgproperties>` object.
.. versionchanged:: 8.2
.. method:: Queue.deqOne()
For consistency and compliance with the PEP 8 naming style, the name of
the method was changed from `deqMany()`. The old name will continue to
work for a period of time.
.. method:: Queue.deqone()
Dequeues at most one message from the queue. If a message is dequeued, it
will be a :ref:`message property<msgproperties>` object; otherwise, it will
be the value None.
.. attribute:: Queue.deqOptions
.. versionchanged:: 8.2
For consistency and compliance with the PEP 8 naming style, the name of
the method was changed from `deqOne()`. The old name will continue to
work for a period of time.
.. attribute:: Queue.deqoptions
This read-only attribute returns a reference to the :ref:`options
<deqoptions>` that will be used when dequeuing messages from the queue.
.. versionchanged:: 8.2
.. method:: Queue.enqOne(message)
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.
Enqueues a single message into the queue. The message must be a
:ref:`message property<msgproperties>` object which has had its payload
attribute set to a value that the queue supports.
.. method:: Queue.enqMany(messages)
.. method:: Queue.enqmany(messages)
Enqueues multiple messages into the queue. The messages parameter must be a
sequence containing :ref:`message property <msgproperties>` objects which
@ -60,26 +78,56 @@ used to enqueue and dequeue messages.
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()`
:meth:`Queue.enqone()` instead. The function :meth:`Queue.deqmany()`
call is not affected.
.. versionchanged:: 8.2
.. attribute:: Queue.enqOptions
For consistency and compliance with the PEP 8 naming style, the name of
the method was changed from `enqMany()`. The old name will continue
to work for a period of time.
.. method:: Queue.enqone(message)
Enqueues a single message into the queue. The message must be a
:ref:`message property<msgproperties>` object which has had its payload
attribute set to a value that the queue supports.
.. versionchanged:: 8.2
For consistency and compliance with the PEP 8 naming style, the name of
the method was changed from `enqOne()`. The old name will continue
to work for a period of time.
.. attribute:: Queue.enqoptions
This read-only attribute returns a reference to the :ref:`options
<enqoptions>` that will be used when enqueuing messages into the queue.
.. versionchanged:: 8.2
For consistency and compliance with the PEP 8 naming style, the name of
the attribute was changed from `enqOptions`. The old name will continue
to work for a period of time.
.. attribute:: Queue.name
This read-only attribute returns the name of the queue.
.. attribute:: Queue.payloadType
.. 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:
@ -223,8 +271,8 @@ Message Properties
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()`.
:meth:`Queue.enqone()` and :meth:`Queue.enqmany()` and
returned by the methods :meth:`Queue.deqone()` and :meth:`Queue.deqmany()`.
.. attribute:: MessageProperties.attempts
@ -280,8 +328,9 @@ Message Properties
.. attribute:: MessageProperties.msgid
This attribute specifies the id of the message in the last queue that
generated this message.
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
@ -307,4 +356,3 @@ Message Properties
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`.

View File

@ -4,6 +4,13 @@
Connection Object
*****************
.. note::
**cx_Oracle has a major new release under a new name and homepage**
`python-oracledb <https://oracle.github.io/python-oracledb/>`__.
**New projects should install python-oracledb instead of cx_Oracle.**
.. note::
Any outstanding changes will be rolled back when the connection object
@ -23,7 +30,7 @@ Connection Object
.. method:: Connection.__exit__()
The exit point for the connection as a context manager. This will close
the connection and roll back any uncomitted transaction.
the connection and roll back any uncommitted transaction.
.. note::
@ -68,14 +75,25 @@ Connection Object
This method is an extension to the DB API definition.
.. attribute:: Connection.callTimeout
.. 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
@ -84,7 +102,7 @@ Connection Object
.. method:: Connection.cancel()
Cancel a long-running transaction.
Break a long-running transaction.
.. note::
@ -195,7 +213,7 @@ Connection Object
.. deprecated:: 7.2
Use the methods :meth:`Queue.deqOne()` or :meth:`Queue.deqMany()`
Use the methods :meth:`Queue.deqone()` or :meth:`Queue.deqmany()`
instead.
.. note::
@ -212,7 +230,7 @@ Connection Object
.. deprecated:: 7.2
Use the attribute :attr:`Queue.deqOptions` instead.
Use the attribute :attr:`Queue.deqoptions` instead.
.. note::
@ -247,6 +265,8 @@ Connection Object
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.
@ -264,7 +284,7 @@ Connection Object
.. deprecated:: 7.2
Use the methods :meth:`Queue.enqOne()` or :meth:`Queue.enqMany()`
Use the methods :meth:`Queue.enqone()` or :meth:`Queue.enqmany()`
instead.
.. note::
@ -281,7 +301,7 @@ Connection Object
.. deprecated:: 7.2
Use the attribute :attr:`Queue.enqOptions` instead.
Use the attribute :attr:`Queue.enqoptions` instead.
.. note::
@ -305,7 +325,7 @@ Connection Object
Return a :ref:`SodaDatabase <sodadb>` object for Simple Oracle Document
Access (SODA). All SODA operations are performed either on the returned
SodaDatabase object or from objects created by the returned SodaDatabase
object. See `here <http://www.oracle.com/pls/topic/lookup?
object. See `here <https://www.oracle.com/pls/topic/lookup?
ctx=dblatest&id=GUID-BE42F8D3-B86B-43B4-B2A3-5760A4DF79FB>`__ for
additional information on SODA.
@ -387,6 +407,8 @@ Connection Object
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.
@ -426,6 +448,8 @@ Connection Object
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.
@ -441,6 +465,8 @@ Connection Object
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.
@ -467,7 +493,7 @@ Connection Object
This method is an extension to the DB API definition.
.. method:: Connection.queue(name, payloadType=None)
.. method:: Connection.queue(name, payload_type=None)
Creates a :ref:`queue <queue>` which is used to enqueue and dequeue
messages in Advanced Queueing.
@ -475,12 +501,18 @@ Connection Object
The name parameter is expected to be a string identifying the queue in
which messages are to be enqueued or dequeued.
The payloadType parameter, if specified, is expected to be an
The payload_type parameter, if specified, is expected to be an
:ref:`object type <objecttype>` that identifies the type of payload the
queue expects. If not specified, RAW data is enqueued and dequeued.
.. versionadded:: 7.2
.. versionchanged:: 8.2
For consistency and compliance with the PEP 8 naming style, the
parameter `payloadType` was renamed to `payload_type`. The old name
will continue to work as a keyword parameter for a period of time.
.. note::
This method is an extension to the DB API definition.
@ -514,12 +546,18 @@ Connection Object
This method is an extension to the DB API definition.
.. method:: Connection.startup(force=False, restrict=False)
.. 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. An example is shown below:
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:
::
@ -541,15 +579,19 @@ Connection Object
.. attribute:: Connection.stmtcachesize
This read-write attribute specifies the size of the statement cache. This
value can make a significant difference in performance (up to 100x) if you
have a small number of statements that you execute repeatedly.
value can make a significant difference in performance if you have a small
number of statements that you execute repeatedly.
The default value is 20.
See :ref:`Statement Caching <stmtcache>` for more information.
.. note::
This attribute is an extension to the DB API definition.
.. method:: Connection.subscribe(namespace=cx_Oracle.SUBSCR_NAMESPACE_DBCHANGE, protocol=cx_Oracle.SUBSCR_PROTO_OCI, callback=None, timeout=0, operations=OPCODE_ALLOPS, port=0, qos=0, ipAddress=None, groupingClass=0, groupingValue=0, groupingType=cx_Oracle.SUBSCR_GROUPING_TYPE_SUMMARY, name=None)
.. method:: Connection.subscribe(namespace=cx_Oracle.SUBSCR_NAMESPACE_DBCHANGE, protocol=cx_Oracle.SUBSCR_PROTO_OCI, callback=None, timeout=0, operations=OPCODE_ALLOPS, port=0, qos=0, ip_address=None, grouping_class=0, grouping_value=0, grouping_type=cx_Oracle.SUBSCR_GROUPING_TYPE_SUMMARY, name=None, client_initiated=False)
Return a new :ref:`subscription object <subscrobj>` that receives
notifications for events that take place in the database that match the
@ -587,16 +629,16 @@ Connection Object
:data:`cx_Oracle.SUBSCR_QOS_QUERY`,
:data:`cx_Oracle.SUBSCR_QOS_BEST_EFFORT`.
The ipAddress 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 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 groupingClass parameter specifies what type of grouping of
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
groupingValue parameter. The groupingType parameter should be one of 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`.
@ -610,8 +652,29 @@ Connection Object
the queue that will be monitored for messages. The queue name may include
the schema, if needed.
*New in version 6.4:* The parameters ipAddress, groupingClass,
groupingValue, groupingType and name were added.
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::
@ -622,7 +685,7 @@ Connection Object
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
explictly closed using the function :meth:`~Connection.close()`, the
explicitly closed using the function :meth:`~Connection.close()`, the
subscription will not be deregistered in the database.
@ -651,6 +714,10 @@ Connection Object
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.
@ -690,4 +757,3 @@ Connection Object
If you connect to Oracle Database 18 or higher with client libraries
12.2 or lower that you will only receive the base version (such as
18.0.0.0.0) instead of the full version (18.3.0.0.0).

View File

@ -5,6 +5,13 @@ Cursor Object
*************
.. note::
**cx_Oracle has a major new release under a new name and homepage**
`python-oracledb <https://oracle.github.io/python-oracledb/>`__.
**New projects should install python-oracledb instead of cx_Oracle.**
.. method:: Cursor.__enter__()
The entry point for the cursor as a context manager. It returns itself.
@ -25,14 +32,20 @@ Cursor Object
.. attribute:: Cursor.arraysize
This read-write attribute specifies the number of rows to fetch at a time
internally and is the default number of rows to fetch with the
:meth:`~Cursor.fetchmany()` call. It defaults to 100 meaning to fetch 100
rows at a time. Note that this attribute can drastically affect the
performance of a query since it directly affects the number of network
round trips that need to be performed. This is the reason for setting it to
100 instead of the 1 that the DB API recommends.
This read-write attribute can be used to tune the number of rows internally
fetched and buffered by internal calls to the database when fetching rows
from SELECT statements and REF CURSORS. The value can drastically affect
the performance of a query since it directly affects the number of network
round trips between Python and the database. For methods like
:meth:`~Cursor.fetchone()` and :meth:`~Cursor.fetchall()` it does not change
how many rows are returned to the application. For
:meth:`~Cursor.fetchmany()` it is the default number of rows to fetch.
Due to the performance benefits, the default ``Cursor.arraysize`` is 100
instead of the 1 that the DB API recommends. This value means that 100 rows
are fetched by each internal call to the database.
See :ref:`Tuning Fetch Performance <tuningfetch>` for more information.
.. attribute:: Cursor.bindarraysize
@ -46,7 +59,7 @@ Cursor Object
The DB API definition does not define this attribute.
.. method:: Cursor.arrayvar(dataType, value, [size])
.. method:: Cursor.arrayvar(typ, value, [size])
Create an array variable associated with the cursor of the given type and
size and return a :ref:`variable object <varobj>`. The value is either an
@ -58,6 +71,12 @@ Cursor Object
the list might be empty and the type cannot be determined automatically) or
returning arrays from PL/SQL.
Array variables can only be used for PL/SQL associative arrays with
contiguous keys. For PL/SQL associative arrays with sparsely populated keys
or for varrays and nested tables, the approach shown in this
`example <https://github.com/oracle/python-cx_Oracle/blob/main/
samples/plsql_collection.py>`__ needs to be used.
.. note::
The DB API definition does not define this method.
@ -87,7 +106,7 @@ Cursor Object
.. method:: Cursor.callfunc(name, returnType, parameters=[], \
keywordParameters={})
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
@ -96,6 +115,15 @@ Cursor Object
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.
@ -107,7 +135,7 @@ Cursor Object
parameter list refers to the return value of the function.
.. method:: Cursor.callproc(name, parameters=[], keywordParameters={})
.. 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
@ -116,6 +144,15 @@ Cursor Object
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.
@ -147,19 +184,22 @@ Cursor Object
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 type objects defined at the module level.
The type will be one of the :ref:`database type constants <dbtypes>`
defined at the module level.
.. method:: Cursor.execute(statement, [parameters], \*\*keywordParameters)
.. method:: Cursor.execute(statement, parameters=[], ** keyword_parameters)
Execute a statement against the database. 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.
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
@ -193,12 +233,14 @@ Cursor Object
Prepare a statement for execution against a database and then execute it
against all parameter mappings or sequences found in the sequence
parameters. The statement is managed in the same way as the
:meth:`~Cursor.execute()` method manages it. If the size of the buffers
allocated for any of the parameters exceeds 2 GB, you will receive the
error "DPI-1015: array size of <n> is too large", where <n> varies with the
size of each element being allocated in the buffer. If you receive this
error, decrease the number of elements in the sequence parameters.
parameters. See :ref:`batchstmnt`.
The statement is managed in the same way as the :meth:`~Cursor.execute()`
method manages it. If the size of the buffers allocated for any of the
parameters exceeds 2 GB, you will receive the error "DPI-1015: array size
of <n> is too large", where <n> varies with the size of each element being
allocated in the buffer. If you receive this error, decrease the number of
elements in the sequence parameters.
If there are no parameters, or parameters have previously been bound, the
number of iterations can be specified as an integer instead of needing to
@ -224,7 +266,7 @@ Cursor Object
a TypeError exception.
.. method:: Cursor.executemanyprepared(numIters)
.. 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
@ -252,21 +294,24 @@ Cursor Object
An exception is raised if the previous call to :meth:`~Cursor.execute()`
did not produce any result set or no call was issued yet.
See :ref:`fetching` for an example.
.. method:: Cursor.fetchmany([numRows=cursor.arraysize])
.. method:: Cursor.fetchmany(numRows=cursor.arraysize)
Fetch the next set of rows of a query result, returning a list of tuples.
An empty list is returned if no more rows are available. Note that the
cursor's arraysize attribute can affect the performance of this operation.
The number of rows to fetch is specified by the parameter. If it is not
given, the cursor's arrysize attribute determines the number of rows to be
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()
@ -276,18 +321,21 @@ Cursor Object
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([numRows=cursor.arraysize])
.. 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. This method was designed for the case where optimal
performance is required as it comes at the expense of compatibility with
the DB API.
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.
@ -321,7 +369,7 @@ Cursor Object
.. method:: Cursor.getbatcherrors()
Retrieve the exceptions that took place after a call to
:meth:`~Cursor.executemany()` with batcherors enabled. This will return a
: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.
@ -378,6 +426,15 @@ Cursor Object
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
@ -387,6 +444,8 @@ Cursor Object
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.
@ -407,6 +466,21 @@ Cursor Object
immediately and an implied commit takes place.
.. attribute:: Cursor.prefetchrows
This read-write attribute can be used to tune the number of rows that the
Oracle Client library fetches when a SELECT statement is executed. This
value can reduce the number of round-trips to the database that are required
to fetch rows but at the cost of additional memory. Setting this value to 0
can be useful when the timing of fetches must be explicitly controlled.
See :ref:`Tuning Fetch Performance <tuningfetch>` for more information.
.. note::
The DB API definition does not define this method.
.. method:: Cursor.prepare(statement, [tag])
This can be used before a call to :meth:`~Cursor.execute()` to define the
@ -416,6 +490,8 @@ Cursor Object
statement will be returned to the statement cache with the given tag. See
the Oracle documentation for more information about the statement cache.
See :ref:`Statement Caching <stmtcache>` for more information.
.. note::
The DB API definition does not define this method.
@ -438,6 +514,8 @@ Cursor Object
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.
@ -516,19 +594,43 @@ Cursor Object
The DB API definition does not define this attribute.
.. method:: Cursor.var(dataType, [size, arraysize, inconverter, outconverter, \
typename, encodingErrors])
.. method:: Cursor.var(typ, [size, arraysize, inconverter, outconverter, \
typename, encoding_errors, bypass_decode])
Create a variable with the specified charactistics. This method was
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 dataType parameter specifies the type of data that should be stored in
the variable. This should be one of the types defined at the module level
(such as :data:`cx_Oracle.STRING`) or a Python type that cx_Oracle knows
how to process (such as str) or an object type returned from the method
:meth:`Connection.gettype()`.
The typ parameter specifies the type of data that should be stored in the
variable. This should be one of the :ref:`database type constants
<dbtypes>`, :ref:`DB API constants <types>`, an object type returned from
the method :meth:`Connection.gettype()` or one of the following Python
types:
.. list-table::
:header-rows: 1
* - Python Type
- Database Type
* - bool
- :attr:`cx_Oracle.DB_TYPE_BOOLEAN`
* - bytes
- :attr:`cx_Oracle.DB_TYPE_RAW`
* - datetime.date
- :attr:`cx_Oracle.DB_TYPE_DATE`
* - datetime.datetime
- :attr:`cx_Oracle.DB_TYPE_DATE`
* - datetime.timedelta
- :attr:`cx_Oracle.DB_TYPE_INTERVAL_DS`
* - decimal.Decimal
- :attr:`cx_Oracle.DB_TYPE_NUMBER`
* - float
- :attr:`cx_Oracle.DB_TYPE_NUMBER`
* - int
- :attr:`cx_Oracle.DB_TYPE_NUMBER`
* - str
- :attr:`cx_Oracle.DB_TYPE_VARCHAR`
The size parameter specifies the length of string and raw variables and is
ignored in all other cases. If not specified for string and raw variables,
@ -547,13 +649,30 @@ Cursor Object
specified when using type :data:`cx_Oracle.OBJECT` unless the type object
was passed directly as the first parameter.
The encodingErrors parameter specifies what should happen when decoding
byte strings fetched from the database into strings (Python 3) or unicode
objects (Python 2). It should be one of the values noted in the builtin
The encoding_errors parameter specifies what should happen when decoding
byte strings fetched from the database into strings. It should be one of
the values noted in the builtin
`decode <https://docs.python.org/3/library/stdtypes.html#bytes.decode>`__
function.
The bypass_decode parameter, if specified, should be passed as a
boolean value. Passing a `True` value causes values of database types
:data:`~cx_Oracle.DB_TYPE_VARCHAR`, :data:`~cx_Oracle.DB_TYPE_CHAR`,
:data:`~cx_Oracle.DB_TYPE_NVARCHAR`, :data:`~cx_Oracle.DB_TYPE_NCHAR` and
:data:`~cx_Oracle.DB_TYPE_LONG` to be returned as `bytes` instead of `str`,
meaning that cx_Oracle doesn't do any decoding. See :ref:`Fetching raw
data <fetching-raw-data>` for more information.
.. versionadded:: 8.2
The parameter `bypass_decode` was added.
.. versionchanged:: 8.2
For consistency and compliance with the PEP 8 naming style, the
parameter `encodingErrors` was renamed to `encoding_errors`. The old
name will continue to work as a keyword parameter for a period of time.
.. note::
The DB API definition does not define this method.

View File

@ -0,0 +1,184 @@
.. _deprecations:
************
Deprecations
************
.. note::
**cx_Oracle has a major new release under a new name and homepage**
`python-oracledb <https://oracle.github.io/python-oracledb/>`__.
**New projects should install python-oracledb instead of cx_Oracle.**
The following tables contains all of the deprecations in the cx_Oracle API,
when they were first deprecated and a comment on what should be used instead,
if applicable. The most recent deprecations are listed first.
.. list-table:: Deprecated in 8.2
:header-rows: 1
:widths: 25 75
:width: 100%
:name: _deprecations_8_2
* - Name
- Comments
* - `encoding` parameter to :meth:`cx_Oracle.connect()`
- No longer needed as the use of encodings other than UTF-8 is
deprecated. Encoding is handled internally between cx_Oracle and Oracle
Database.
* - `nencoding` parameter to :meth:`cx_Oracle.connect()`
- No longer needed as the use of encodings other than UTF-8 is
deprecated.
* - `encoding` parameter to :meth:`cx_Oracle.SessionPool()`
- No longer needed as the use of encodings other than UTF-8 is
deprecated.
* - `nencoding` parameter to :meth:`cx_Oracle.SessionPool()`
- No longer needed as the use of encodings other than UTF-8 is
deprecated.
* - Connection.maxBytesPerCharacter
- No longer needed as the use of encodings other than UTF-8 is
deprecated. The constant value 4 can be used instead.
* - Positional parameters to :meth:`cx_Oracle.connect()`
- Replace with keyword parameters in order to comply with the Python
database API.
* - Positional parameters to :meth:`cx_Oracle.SessionPool()`
- Replace with keyword parameters in order to comply with the Python
database API.
* - `threaded` parameter to :meth:`cx_Oracle.SessionPool()`
- The value of this parameter is ignored. Threading is now always used.
* - `waitTimeout` parameter to :meth:`cx_Oracle.SessionPool()`
- Replace with parameter name `wait_timeout`
* - `maxLifetimeSession` parameter to :meth:`cx_Oracle.SessionPool()`
- Replace with parameter name `max_lifetime_session`
* - `sessionCallback` parameter to :meth:`cx_Oracle.SessionPool()`
- Replace with parameter name `session_callback`
* - `maxSessionsPerShard` parameter to :meth:`cx_Oracle.SessionPool()`
- Replace with parameter name `max_sessions_per_shard`
* - `SessionPool.tnsentry`
- Replace with :data:`SessionPool.dsn`
* - `payloadType` parameter to :meth:`Connection.queue()`
- Replace with parameter name `payload_type` if using keyword parameters.
* - `ipAddress` parameter to :meth:`Connection.subscribe()`
- Replace with parameter name `ip_address`
* - `groupingClass` parameter to :meth:`Connection.subscribe()`
- Replace with parameter name `grouping_class`
* - `groupingValue` parameter to :meth:`Connection.subscribe()`
- Replace with parameter name `grouping_value`
* - `groupingType` parameter to :meth:`Connection.subscribe()`
- Replace with parameter name `grouping_type`
* - `clientInitiated` parameter to :meth:`Connection.subscribe()`
- Replace with parameter name `client_initiated`
* - `Connection.callTimeout`
- Replace with :data:`Connection.call_timeout`
* - `Connection.tnsentry`
- Replace with :data:`Connection.dsn`
* - `keywordParameters` parameter to :meth:`Cursor.callfunc()`
- Replace with parameter name `keyword_parameters`
* - `keywordParameters` parameter to :meth:`Cursor.callproc()`
- Replace with parameter name `keyword_parameters`
* - `encodingErrors` parameter to :meth:`Cursor.var()`
- Replace with parameter name `encoding_errors`
* - `Cursor.fetchraw()`
- Replace with :meth:`Cursor.fetchmany()`
* - `newSize` parameter to :meth:`LOB.trim()`
- Replace with parameter name `new_size`
* - `Queue.deqMany`
- Replace with :meth:`Queue.deqmany()`
* - `Queue.deqOne`
- Replace with :meth:`Queue.deqone()`
* - `Queue.enqMany`
- Replace with :meth:`Queue.enqmany()`
* - `Queue.enqOne`
- Replace with :meth:`Queue.enqone()`
* - `Queue.deqOptions`
- Replace with :data:`Queue.deqoptions`
* - `Queue.enqOptions`
- Replace with :meth:`Queue.enqoptions`
* - `Queue.payloadType`
- Replace with :meth:`Queue.payload_type`
* - `Subscription.ipAddress`
- Replace with :attr:`Subscription.ip_address`
* - `Message.consumerName`
- Replace with :attr:`Message.consumer_name`
* - `Message.queueName`
- Replace with :attr:`Message.queue_name`
* - `Variable.actualElements`
- Replace with :attr:`Variable.actual_elements`
* - `Variable.bufferSize`
- Replace with :attr:`Variable.buffer_size`
* - `Variable.numElements`
- Replace with :attr:`Variable.num_elements`
.. list-table:: Deprecated in 8.0
:header-rows: 1
:widths: 25 75
:width: 100%
:name: _deprecations_8_0
* - Name
- Comments
* - cx_Oracle.BFILE
- Replace with :data:`cx_Oracle.DB_TYPE_BFILE`
* - cx_Oracle.BLOB
- Replace with :data:`cx_Oracle.DB_TYPE_BLOB`
* - cx_Oracle.BOOLEAN
- Replace with :data:`cx_Oracle.DB_TYPE_BOOLEAN`
* - cx_Oracle.CLOB
- Replace with :data:`cx_Oracle.DB_TYPE_CLOB`
* - cx_Oracle.CURSOR
- Replace with :data:`cx_Oracle.DB_TYPE_CURSOR`
* - cx_Oracle.FIXED_CHAR
- Replace with :data:`cx_Oracle.DB_TYPE_CHAR`
* - cx_Oracle.FIXED_NCHAR
- Replace with :data:`cx_Oracle.DB_TYPE_NCHAR`
* - cx_Oracle.INTERVAL
- Replace with :data:`cx_Oracle.DB_TYPE_INTERVAL_DS`
* - cx_Oracle.LONG_BINARY
- Replace with :data:`cx_Oracle.DB_TYPE_LONG_RAW`
* - cx_Oracle.LONG_STRING
- Replace with :data:`cx_Oracle.DB_TYPE_LONG`
* - cx_Oracle.NATIVE_FLOAT
- Replace with :data:`cx_Oracle.DB_TYPE_BINARY_DOUBLE`
* - cx_Oracle.NATIVE_INT
- Replace with :data:`cx_Oracle.DB_TYPE_BINARY_INTEGER`
* - cx_Oracle.NCHAR
- Replace with :data:`cx_Oracle.DB_TYPE_NVARCHAR`
* - cx_Oracle.NCLOB
- Replace with :data:`cx_Oracle.DB_TYPE_NCLOB`
* - cx_Oracle.OBJECT
- Replace with :data:`cx_Oracle.DB_TYPE_OBJECT`
* - cx_Oracle.TIMESTAMP
- Replace with :data:`cx_Oracle.DB_TYPE_TIMESTAMP`
.. list-table:: Deprecated in 7.2
:header-rows: 1
:widths: 25 75
:width: 100%
:name: _deprecations_7_2
* - Name
- Comments
* - Connection.deq()
- Replace with :meth:`Queue.deqone()` or :meth:`Queue.deqmany()`.
* - Connection.deqoptions()
- Replace with attribute :attr:`Queue.deqoptions`.
* - Connection.enq()
- Replace with :meth:`Queue.enqone()` or :meth:`Queue.enqmany()`.
* - Connection.enqoptions()
- Replace with attribute :attr:`Queue.enqoptions`.
.. list-table:: Deprecated in 6.4
:header-rows: 1
:widths: 25 75
:width: 100%
:name: _deprecations_6_4
* - Name
- Comments
* - Cursor.executemanyprepared()
- Replace with :meth:`~Cursor.executemany()` with None for the statement
argument and an integer for the parameters argument.

View File

@ -4,6 +4,15 @@
LOB Objects
***********
.. note::
**cx_Oracle has a major new release under a new name and homepage**
`python-oracledb <https://oracle.github.io/python-oracledb/>`__.
**New projects should install python-oracledb instead of cx_Oracle.**
See :ref:`lobdata` for more information about using LOBs.
.. note::
This object is an extension the DB API. It is returned whenever Oracle
@ -72,12 +81,20 @@ LOB Objects
for all but supplemental characters.
.. method:: LOB.trim([newSize=0])
.. method:: LOB.trim(new_size=0)
Trim the LOB to the new size.
.. method:: LOB.write(data, [offset=1])
.. attribute:: LOB.type
This read-only attribute returns the type of the LOB as one of the
:ref:`database type constants <dbtypes>`.
.. versionadded:: 8.0
.. method:: LOB.write(data, offset=1)
Write the data to the LOB object at the given offset. The offset is in
bytes for BLOB type LOBs and in UCS-2 code points for CLOB and NCLOB type
@ -86,4 +103,3 @@ LOB Objects
offset will have to be chosen carefully to avoid splitting a character.
Note that if you want to make the LOB value smaller, you must use the
:meth:`~LOB.trim()` function.

File diff suppressed because it is too large Load Diff

View File

@ -4,6 +4,13 @@
Object Type Objects
*******************
.. note::
**cx_Oracle has a major new release under a new name and homepage**
`python-oracledb <https://oracle.github.io/python-oracledb/>`__.
**New projects should install python-oracledb instead of cx_Oracle.**
.. note::
This object is an extension to the DB API. It is returned by the
@ -19,8 +26,8 @@ Object Type Objects
.. attribute:: ObjectType.attributes
This read-only attribute returns a list of the attributes that make up the
object type. Each attribute has a name attribute on it.
This read-only attribute returns a list of the :ref:`attributes
<objectattr>` that make up the object type.
.. attribute:: ObjectType.iscollection
@ -34,6 +41,17 @@ Object Type Objects
This read-only attribute returns the name of the type.
.. attribute:: ObjectType.element_type
This read-only attribute returns the type of elements found in collections
of this type, if :attr:`~ObjectType.iscollection` is ``True``; otherwise,
it returns ``None``. If the collection contains objects, this will be
another object type; otherwise, it will be one of the
:ref:`database type constants <dbtypes>`.
.. versionadded:: 8.0
.. method:: ObjectType.newobject([sequence])
Return a new Oracle object of the given type. This object can then be
@ -49,7 +67,7 @@ Object Type Objects
Object Objects
==============
--------------
.. note::
@ -71,6 +89,8 @@ Object Objects
Return a dictionary where the collection's indexes are the keys and the
elements are its values.
.. versionadded:: 7.0
.. method:: Object.aslist()
@ -150,3 +170,28 @@ Object Objects
Remove the specified number of elements from the end of the collection.
.. _objectattr:
Object Attribute Objects
------------------------
.. note::
This object is an extension to the DB API. The elements of
:attr:`ObjectType.attributes` are instances of this type.
.. attribute:: ObjectAttribute.name
This read-only attribute returns the name of the attribute.
.. attribute:: ObjectAttribute.type
This read-only attribute returns the type of the attribute. This will be an
:ref:`Oracle Object Type <objecttype>` if the variable binds
Oracle objects; otherwise, it will be one of the
:ref:`database type constants <dbtypes>`.
.. versionadded:: 8.0

View File

@ -4,12 +4,21 @@
SessionPool Object
******************
.. note::
**cx_Oracle has a major new release under a new name and homepage**
`python-oracledb <https://oracle.github.io/python-oracledb/>`__.
**New projects should install python-oracledb instead of cx_Oracle.**
.. note::
This object is an extension to the DB API.
Connection pooling in cx_Oracle is handled by SessionPool objects.
See :ref:`connpool` for information on connection pooling.
.. method:: SessionPool.acquire(user=None, password=None, cclass=None, \
purity=cx_Oracle.ATTR_PURITY_DEFAULT, tag=None, matchanytag=False, \
@ -39,8 +48,9 @@ SessionPool Object
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. Currently only strings are supported for the key
values.
shard to connect to. The key values can be strings, numbers, bytes or
dates.
.. attribute:: SessionPool.busy
@ -50,7 +60,7 @@ SessionPool Object
.. method:: SessionPool.close(force=False)
Close the session pool now, rather than when the last reference to it is
released, which makes it unsable for further work.
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.
@ -68,6 +78,20 @@ SessionPool Object
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
@ -98,11 +122,22 @@ SessionPool Object
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
@ -122,6 +157,77 @@ SessionPool Object
the session pool.
.. attribute:: SessionPool.ping_interval
This read-write integer attribute specifies the pool ping interval in
seconds. When a connection is acquired from the pool, a check is first made
to see how long it has been since the connection was put into the pool. If
this idle time exceeds ``ping_interval``, then a :ref:`round-trip
<roundtrips>` ping to the database is performed. If the connection is
unusable, it is discarded and a different connection is selected to be
returned by :meth:`SessionPool.acquire()`. Setting ``ping_interval`` to a
negative value disables pinging. Setting it to 0 forces a ping for every
``acquire()`` and is not recommended.
Prior to cx_Oracle 8.2, the ping interval was fixed at 60 seconds.
.. versionadded:: 8.2
.. method:: SessionPool.reconfigure([min, max, increment, getmode, timeout, \
wait_timeout, max_lifetime_session, max_sessions_per_shard, \
soda_metadata_cache, stmtcachesize, ping_interval])
Reconfigures various parameters of a connection pool. The pool size can be
altered with ``reconfigure()`` by passing values for
:data:`~SessionPool.min`, :data:`~SessionPool.max` or
:data:`~SessionPool.increment`. The :data:`~SessionPool.getmode`,
:data:`~SessionPool.timeout`, :data:`~SessionPool.wait_timeout`,
:data:`~SessionPool.max_lifetime_session`,
:data:`~SessionPool.max_sessions_per_shard`,
:data:`~SessionPool.soda_metadata_cache`, :data:`~SessionPool.stmtcachesize`
and :data:`~SessionPool.ping_interval` attributes can be set directly or
with ``reconfigure()``.
All parameters are optional. Unspecified parameters will leave those pool
attributes unchanged. The parameters are processed in two stages. After any
size change has been processed, reconfiguration on the other parameters is
done sequentially. If an error such as an invalid value occurs when changing
one attribute, then an exception will be generated but any already changed
attributes will retain their new values.
During reconfiguration of a pool's size, the behavior of
:meth:`SessionPool.acquire()` depends on the ``getmode`` in effect when
``acquire()`` is called:
* With mode :data:`~cx_Oracle.SPOOL_ATTRVAL_FORCEGET`, an ``acquire()`` call
will wait until the pool has been reconfigured.
* With mode :data:`~cx_Oracle.SPOOL_ATTRVAL_TIMEDWAIT`, an ``acquire()`` call
will try to acquire a connection in the time specified by
:data:`~SessionPool.wait_timeout` and return an error if the time taken
exceeds that value.
* With mode :data:`~cx_Oracle.SPOOL_ATTRVAL_WAIT`, an ``acquire()`` call
will wait until after the pool has been reconfigured and a connection is
available.
* With mode :data:`~cx_Oracle.SPOOL_ATTRVAL_NOWAIT`, if the number of busy
connections is less than the pool size, ``acquire()`` will return a new
connection after pool reconfiguration is complete.
Closing connections with :meth:`SessionPool.release()` or
:meth:`Connection.close()` will wait until any pool size reconfiguration is
complete.
Closing the connection pool with :meth:`SessionPool.close()` will wait until
reconfiguration is complete.
See :ref:`Connection Pool Reconfiguration <poolreconfiguration>`.
.. versionadded:: 8.2
.. method:: SessionPool.release(connection, tag=None)
Release the connection back to the pool now, rather than whenever __del__
@ -145,13 +251,25 @@ SessionPool Object
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 as the starting point for any connections that are created by
the session pool. Once created, the connection's statement cache size can
only be changed by setting the stmtcachesize attribute on the connection
itself.
will be used for connections obtained from the pool.
See :ref:`Statement Caching <stmtcache>` for more information.
.. versionadded:: 6.0
@ -169,6 +287,10 @@ SessionPool Object
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
@ -185,4 +307,3 @@ SessionPool Object
:data:`cx_Oracle.SPOOL_ATTRVAL_TIMEDWAIT`.
.. versionadded:: 6.4

View File

@ -4,15 +4,75 @@
SODA
****
An overview of Simple Oracle Document Access (SODA) can be found
`here
<https://docs.oracle.com/en/database/oracle/simple-oracle-document-access>`__.
.. note::
**cx_Oracle has a major new release under a new name and homepage**
`python-oracledb <https://oracle.github.io/python-oracledb/>`__.
**New projects should install python-oracledb instead of cx_Oracle.**
`Oracle Database Simple Oracle Document Access (SODA)
<https://docs.oracle.com/en/database/oracle/simple-oracle-document-access>`__
allows documents to be inserted, queried, and retrieved from Oracle Database
using a set of NoSQL-style cx_Oracle methods. By default, documents are JSON
strings. See the :ref:`user manual <sodausermanual>` for examples.
.. _sodarequirements:
-----------------
SODA Requirements
-----------------
To use SODA, the role SODA_APP must be granted to the user. To create
collections, users need the CREATE TABLE privilege. These can be granted by a
DBA:
.. code-block:: sql
SQL> grant soda_app, create table to myuser;
Advanced users who are using Oracle sequences for keys will also need the CREATE
SEQUENCE privilege.
SODA requires Oracle Client 18.3 or higher and Oracle Database 18.1 and higher.
The role SODA_APP must be granted to the user.
See
`this tracking issue <https://github.com/oracle/python-cx_Oracle/issues/309>`__ for known issues with SODA.
.. note::
If you are using Oracle Database 21c (or later) and create new collections
you need to do one of the following:
- Use Oracle Client libraries 21c (or later).
- Or, explicitly use collection metadata when creating collections and set
the data storage type to BLOB, for example::
{
"keyColumn": {
"name":"ID"
},
"contentColumn": {
"name": "JSON_DOCUMENT",
"sqlType": "BLOB"
},
"versionColumn": {
"name": "VERSION",
"method": "UUID"
},
"lastModifiedColumn": {
"name": "LAST_MODIFIED"
},
"creationTimeColumn": {
"name": "CREATED_ON"
}
}
- Or, set the database initialization parameter `compatible
<https://www.oracle.com/pls/topic/lookup?ctx=dblatest&
id=GUID-A2E90F08-BC9F-4688-A9D0-4A948DD3F7A9>`__ to 19 or lower.
Otherwise you may get errors such as "ORA-40842: unsupported value JSON in
the metadata for the field sqlType" or "ORA-40659: Data type does not match
the specification in the collection metadata".
.. _sodadb:
@ -110,7 +170,7 @@ SODA Collection Object
.. note::
This object is an extension the DB API. It is used to represent SODA
collections and is created by methods
collections and is created by methods
:meth:`SodaDatabase.createCollection()` and
:meth:`SodaDatabase.openCollection()`.
@ -194,19 +254,37 @@ SODA Collection Object
.. versionadded:: 7.2
.. method:: SodaCollection.insertManyAndGet(docs)
.. method:: SodaCollection.insertManyAndGet(docs, hint=None)
Similarly to :meth:`~SodaCollection.insertMany()` this method inserts a
list of documents into the collection at one time. The only difference is
that it returns a list of :ref:`SODA Document objects <sodadoc>`. Note that
for performance reasons the returned documents do not contain the content.
The hint parameter, if specified, supplies a hint to the database when
processing the SODA operation. This is expected to be a string in the same
format as a SQL hint but without any comment characters, for example
``hint="MONITOR"``. Pass only the hint ``"MONITOR"`` (turn on monitoring)
or ``"NO_MONITOR"`` (turn off monitoring). See the Oracle Database SQL
Tuning Guide documentation `MONITOR and NO_MONITOR Hints
<https://www.oracle.com/pls/topic/lookup?
ctx=dblatest&id=GUID-19E0F73C-A959-41E4-A168-91E436DEE1F1>`__
and `Monitoring Database Operations
<https://www.oracle.com/pls/topic/lookup?
ctx=dblatest&id=GUID-C941CE9D-97E1-42F8-91ED-4949B2B710BF>`__
for more information.
.. note::
This method requires Oracle Client 18.5 and higher.
.. versionadded:: 7.2
.. versionchanged:: 8.2
The parameter `hint` was added. Use of the hint parameter requires
Oracle Client 21.3 or higher (or Oracle Client 19 from 19.11).
.. method:: SodaCollection.insertOne(doc)
@ -216,19 +294,37 @@ SODA Collection Object
.. versionadded:: 7.0
.. method:: SodaCollection.insertOneAndGet(doc)
.. method:: SodaCollection.insertOneAndGet(doc, hint=None)
Similarly to :meth:`~SodaCollection.insertOne()` this method inserts a
given document into the collection. The only difference is that it
returns a :ref:`SODA Document object <sodadoc>`. Note that for performance
reasons the returned document does not contain the content.
The hint parameter, if specified, supplies a hint to the database when
processing the SODA operation. This is expected to be a string in the same
format as a SQL hint but without any comment characters, for example
``hint="MONITOR"``. Pass only the hint ``"MONITOR"`` (turn on monitoring)
or ``"NO_MONITOR"`` (turn off monitoring). See the Oracle Database SQL
Tuning Guide documentation `MONITOR and NO_MONITOR Hints
<https://www.oracle.com/pls/topic/lookup?
ctx=dblatest&id=GUID-19E0F73C-A959-41E4-A168-91E436DEE1F1>`__
and `Monitoring Database Operations
<https://www.oracle.com/pls/topic/lookup?
ctx=dblatest&id=GUID-C941CE9D-97E1-42F8-91ED-4949B2B710BF>`__
for more information.
.. versionadded:: 7.0
.. versionchanged:: 8.2
The parameter `hint` was added. Use of the hint parameter requires
Oracle Client 21.3 or higher (or Oracle Client 19 from 19.11).
.. attribute:: SodaCollection.metadata
This read-only attribute returns a dicationary containing the metadata that
This read-only attribute returns a dictionary containing the metadata that
was used to create the collection. See this `collection metadata reference
<https://www.oracle.com/pls/topic/
lookup?ctx=dblatest&id=GUID-49EFF3D3-9FAB-4DA6-BDE2-2650383566A3>`__
@ -244,6 +340,58 @@ SODA Collection Object
.. versionadded:: 7.0
.. method:: SodaCollection.save(doc)
Saves a document into the collection. This method is equivalent to
:meth:`~SodaCollection.insertOne()` except that if client-assigned keys are
used, and the document with the specified key already exists in the
collection, it will be replaced with the input document.
This method requires Oracle Client 19.9 or higher in addition to the usual
SODA requirements.
.. versionadded:: 8.0
.. method:: SodaCollection.saveAndGet(doc, hint=None)
Saves a document into the collection. This method is equivalent to
:meth:`~SodaCollection.insertOneAndGet()` except that if client-assigned
keys are used, and the document with the specified key already exists in
the collection, it will be replaced with the input document.
The hint parameter, if specified, supplies a hint to the database when
processing the SODA operation. This is expected to be a string in the same
format as a SQL hint but without any comment characters, for example
``hint="MONITOR"``. Pass only the hint ``"MONITOR"`` (turn on monitoring)
or ``"NO_MONITOR"`` (turn off monitoring). See the Oracle Database SQL
Tuning Guide documentation `MONITOR and NO_MONITOR Hints
<https://www.oracle.com/pls/topic/lookup?
ctx=dblatest&id=GUID-19E0F73C-A959-41E4-A168-91E436DEE1F1>`__
and `Monitoring Database Operations
<https://www.oracle.com/pls/topic/lookup?
ctx=dblatest&id=GUID-C941CE9D-97E1-42F8-91ED-4949B2B710BF>`__
for more information.
This method requires Oracle Client 19.9 or higher in addition to the usual
SODA requirements.
.. versionadded:: 8.0
.. versionchanged:: 8.2
The parameter `hint` was added. Use of the hint parameter requires
Oracle Client 21.3 or higher (or Oracle Client 19 from 19.11).
.. method:: SodaCollection.truncate()
Removes all of the documents in the collection, similarly to what is done
for rows in a table by the TRUNCATE TABLE statement.
.. versionadded:: 8.0
.. _sodadoc:
--------------------
@ -383,6 +531,20 @@ SODA Operation Object
.. 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
@ -425,6 +587,28 @@ SODA Operation Object
.. versionadded:: 7.0
.. method:: SodaOperation.hint(value)
Specifies a hint that will be provided to the SODA operation when it is
performed. This is expected to be a string in the same format as a SQL hint
but without any comment characters, for example ``hint("MONITOR")``. Pass
only the hint ``"MONITOR"`` (turn on monitoring) or ``"NO_MONITOR"`` (turn
off monitoring). See the Oracle Database SQL Tuning Guide documentation
`MONITOR and NO_MONITOR Hints
<https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-19E0F73C-A959-41E4-A168-91E436DEE1F1>`__
and `Monitoring Database Operations
<https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-C941CE9D-97E1-42F8-91ED-4949B2B710BF>`__
for more information.
As a convenience, the SodaOperation object is returned so that further
criteria can be specified by chaining methods together.
Use of this method requires Oracle Client 21.3 or higher (or Oracle Client
19 from 19.11).
.. versionadded:: 8.2
.. method:: SodaOperation.key(value)
Specifies that the document with the specified key should be returned.
@ -520,4 +704,3 @@ SODA Operation Object
criteria can be specified by chaining methods together.
.. versionadded:: 7.0

View File

@ -4,6 +4,13 @@
Subscription Object
*******************
.. note::
**cx_Oracle has a major new release under a new name and homepage**
`python-oracledb <https://oracle.github.io/python-oracledb/>`__.
**New projects should install python-oracledb instead of cx_Oracle.**
.. note::
This object is an extension the DB API.
@ -29,7 +36,7 @@ Subscription Object
subscriptions, the value is 0.
.. attribute:: Subscription.ipAddress
.. 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,
@ -37,6 +44,12 @@ Subscription Object
.. 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
@ -98,7 +111,7 @@ Subscription Object
.. _msgobjects:
Message Objects
===============
---------------
.. note::
@ -106,7 +119,7 @@ Message Objects
to the callback procedure specified when a subscription is created.
.. attribute:: Message.consumerName
.. 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
@ -115,6 +128,12 @@ Message Objects
.. 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
@ -130,7 +149,7 @@ Message Objects
:data:`~cx_Oracle.SUBSCR_QOS_QUERY` when the subscription was created.
.. attribute:: Message.queueName
.. 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
@ -138,6 +157,12 @@ Message Objects
.. 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
@ -177,7 +202,7 @@ Message Objects
Message Table Objects
=====================
---------------------
.. note::
@ -206,7 +231,7 @@ Message Table Objects
Message Row Objects
===================
-------------------
.. note::
@ -227,19 +252,19 @@ Message Row Objects
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
result set changed. The value will match the value returned by
Subscription.registerquery when the related query was registered.
@ -255,4 +280,3 @@ Message Query Objects
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.

View File

@ -4,12 +4,19 @@
Variable Objects
****************
.. note::
**cx_Oracle has a major new release under a new name and homepage**
`python-oracledb <https://oracle.github.io/python-oracledb/>`__.
**New projects should install python-oracledb instead of cx_Oracle.**
.. note::
The DB API definition does not define this object.
.. attribute:: Variable.actualElements
.. 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
@ -17,11 +24,24 @@ Variable Objects
:func:`Cursor.arrayvar()`. For all other variables this value will be
identical to the attribute :attr:`~Variable.numElements`.
.. attribute:: Variable.bufferSize
.. 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])
@ -41,17 +61,23 @@ Variable Objects
attribute is None, the value is bound directly without any conversion.
.. attribute:: Variable.numElements
.. 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
from the Oracle to Python. The method signature is converter(value)
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.
@ -70,9 +96,14 @@ Variable Objects
.. attribute:: Variable.type
This read-only attribute returns the type of the variable for those
variables that bind Oracle objects (it is not present for any other type of
variable).
This read-only attribute returns the type of the variable. This will be an
:ref:`Oracle Object Type <objecttype>` if the variable binds
Oracle objects; otherwise, it will be one of the
:ref:`database type constants <dbtypes>`.
.. versionchanged:: 8.0
Database type constants are now used when the variable is not used for
binding Oracle objects.
.. attribute:: Variable.values
@ -81,4 +112,3 @@ Variable Objects
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.

View File

@ -28,21 +28,21 @@ templates_path = ['.templates']
# The suffix of source filenames.
source_suffix = '.rst'
# The master toctree document.
master_doc = 'index'
# The root toctree document.
root_doc = master_doc = 'index'
# General substitutions.
project = 'cx_Oracle'
copyright = u'2016, 2019, 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, 2020, Oracle and/or its affiliates. All rights reserved. Portions Copyright © 2007-2015, Anthony Tuininga. All rights reserved. Portions Copyright © 2001-2007, Computronix (Canada) Ltd., Edmonton, Alberta, Canada. All rights reserved'
author = 'Oracle'
# The default replacements for |version| and |release|, also used in various
# other places throughout the built documents.
#
# The short X.Y version.
version = '7.2'
version = '8.3'
# The full version, including alpha/beta/rc tags.
release = '7.2.0'
release = '8.3.0'
# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
@ -108,6 +108,7 @@ html_copy_source = False
# Output file base name for HTML help builder.
htmlhelp_basename = 'cx_Oracledoc'
numfig = True
# Options for LaTeX output
# ------------------------

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

View File

@ -1,34 +1,70 @@
Welcome to cx_Oracle's documentation!
=====================================
.. note::
**cx_Oracle has a major new release under a new name and homepage**
`python-oracledb <https://oracle.github.io/python-oracledb/>`__.
**New projects should install python-oracledb instead of cx_Oracle.**
**cx_Oracle** is a module that enables access to Oracle Database and conforms
to the Python database API specification. This module is currently tested
against Oracle Client 19, 18, 12, and 11.2, and Python 2.7, 3.5, 3.6 and
3.7.
against Oracle Client 21c, 19c, 18c, 12c, and 11.2, and Python 3.6, 3.7, 3.8,
3.9 and 3.10. Older versions of cx_Oracle may be used with previous Python
releases.
**cx_Oracle** is distributed under an open-source :ref:`license <license>`
(the BSD license).
(the BSD license). A detailed description of cx_Oracle changes can be found in
the :ref:`release notes <releasenotes>`.
Contents:
.. toctree::
:maxdepth: 2
User Guide
==========
installation.rst
module.rst
connection.rst
cursor.rst
variable.rst
session_pool.rst
subscription.rst
lob.rst
objecttype.rst
aq.rst
soda.rst
whatsnew.rst
releasenotes.rst
license.rst
.. toctree::
:maxdepth: 3
user_guide/introduction.rst
user_guide/installation.rst
user_guide/initialization.rst
user_guide/connection_handling.rst
user_guide/sql_execution.rst
user_guide/plsql_execution.rst
user_guide/bind.rst
user_guide/lob_data.rst
user_guide/json_data_type.rst
user_guide/soda.rst
user_guide/xml_data_type.rst
user_guide/batch_statement.rst
user_guide/exception_handling.rst
user_guide/aq.rst
user_guide/cqn.rst
user_guide/txn_management.rst
user_guide/tuning.rst
user_guide/globalization.rst
user_guide/startup.rst
user_guide/ha.rst
user_guide/tracing_sql.rst
API Manual
==========
.. toctree::
:maxdepth: 3
api_manual/module.rst
api_manual/connection.rst
api_manual/cursor.rst
api_manual/variable.rst
api_manual/session_pool.rst
api_manual/subscription.rst
api_manual/lob.rst
api_manual/object_type.rst
api_manual/aq.rst
api_manual/soda.rst
api_manual/deprecations.rst
Indices and tables
@ -37,4 +73,3 @@ Indices and tables
* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`

View File

@ -1,692 +0,0 @@
.. _installation:
************************
cx_Oracle 7 Installation
************************
.. contents:: :local:
Overview
========
To use cx_Oracle 7 with Python and Oracle Database you need:
- Python 2.7 or 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
<http://www.oracle.com/technetwork/database/database-technologies/instant-client/overview/index.html>`__,
or those included in Oracle Database if Python is on the same
machine as the database. Oracle client libraries versions 19, 18, 12,
and 11.2 are supported on Linux, Windows and macOS. Users have
also reported success with other platforms.
- An Oracle Database. Oracle's standard client-server version
interoperability allows cx_Oracle to connect to both older and newer
databases.
Quick Start cx_Oracle Installation
==================================
- An installation of `Python <https://www.python.org/downloads>`__ is
needed. Python 2.7 and Python 3.5 and higher are supported by cx_Oracle 7.
- Install cx_Oracle from `PyPI
<https://pypi.python.org/pypi/cx_Oracle>`__ with::
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.
- Add Oracle 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 move the files to ``~/lib``
or ``/usr/local/lib``.
- If your database is on a remote computer, then download and unzip the client
libraries from the free `Oracle Instant Client
<http://www.oracle.com/technetwork/database/database-technologies/instant-client/overview/index.html>`__
"Basic" or "Basic Light" package for your operating system
architecture.
Instant Client on Windows requires an appropriate `Microsoft
Windows Redistributables
<https://oracle.github.io/odpi/doc/installation.html#windows>`__.
On Linux, the ``libaio`` (sometimes called ``libaio1``) package
is needed.
- Alternatively use the client libraries already available in a
locally installed database such as the free `Oracle XE
<https://www.oracle.com/database/technologies/appdev/xe.html>`__
release.
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.
The database abstraction layer in cx_Oracle is `ODPI-C
<https://github.com/oracle/odpi>`__, which means that the `ODPI-C
installation instructions
<https://oracle.github.io/odpi/doc/installation.html>`__ can be useful
to review.
- Create a script like the one below::
# myscript.py
from __future__ import print_function
import cx_Oracle
# Connect as user "hr" with password "welcome" to the "oraclepdb" service running on this computer.
connection = cx_Oracle.connect("hr", "welcome", "localhost/orclpdb")
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 the service name of the Oracle Database instance.
Substitute your username, password and connection string in the
code. Run the Python script, for example::
python myscript.py
You can learn how to use cx_Oracle from the :ref:`API documentation <module>`
and `samples
<https://github.com/oracle/python-cx_Oracle/blob/master/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
<http://www.oracle.com/technetwork/database/database-technologies/instant-client/overview/index.html>`__
"Basic" or "Basic Light" package. The libraries are also available in
any Oracle Database installation or full Oracle Client installation.
Oracle's standard client-server network interoperability allows
connections between different versions of Oracle Client libraries and
Oracle Database. For certified configurations see Oracle Support's
`Doc ID 207303.1
<https://support.oracle.com/epmos/faces/DocumentDisplay?id=207303.1>`__.
In summary, Oracle Client 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
<http://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-9D12F489-EC02-46BE-8CD4-5AECED0E2BA2>`__ external configuration
file available with 12.1 or later clients, session pool improvements,
call timeouts with 18 or later clients, and `other enhancements
<http://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-D60519C3-406F-4588-8DA1-D475D5A3E1F6>`__.
The cx_Oracle function :func:`~cx_Oracle.clientversion()` can be used
to determine which Oracle Client version is in use and 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 some Oracle
features that are not supported by a particular client/server
combination may result in runtime errors. These include:
- when attempting to access attributes that are not supported by the
current Oracle Client library you will get the error "ORA-24315: illegal
attribute type"
- when attempting to use implicit results with Oracle Client 11.2
against Oracle Database 12c you will get the error "ORA-29481:
Implicit results cannot be returned to client"
- when attempting to get array DML row counts with Oracle Client
11.2 you will get the error "DPI-1050: Oracle Client library must be at
version 12.1 or higher"
Installing cx_Oracle on Linux
=============================
This section discusses the generic installation method on Linux.
Using Python and cx_Oracle RPM packages on Oracle Linux is discussed
in :ref:`oraclelinux`.
Install cx_Oracle
-----------------
The generic way to install cx_Oracle on Linux is to use Python's `Pip
<http://pip.readthedocs.io/en/latest/installing/>`__ package to
install cx_Oracle from `PyPI
<https://pypi.python.org/pypi/cx_Oracle>`__::
python -m pip install cx_Oracle --upgrade
This will download and install a pre-compiled binary `if one is
available <https://pypi.python.org/pypi/cx_Oracle>`__ for your
architecture. If a pre-compiled binary is not available, the source
will be downloaded, compiled, and the resulting binary installed.
Compiling cx_Oracle requires the `Python.h` header file. If you are
using the default python package, this file is in the ``python-devel``
package or equivalent.
Install Oracle Client
---------------------
Using cx_Oracle requires Oracle Client libraries to be installed.
These provide the necessary network connectivity allowing cx_Oracle
to access an Oracle Database instance. 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
<http://www.oracle.com/technetwork/database/database-technologies/instant-client/overview/index.html>`__
"Basic" or "Basic Light" package for your operating system
architecture. Use the RPM or ZIP packages, based on your
preferences.
- Alternatively use the client libraries already available in a
locally installed database such as the free `Oracle XE
<https://www.oracle.com/database/technologies/appdev/xe.html>`__
release.
Oracle Instant Client Zip Files
+++++++++++++++++++++++++++++++
To use cx_Oracle with Oracle Instant Client zip files:
1. Download an Oracle 19, 18, 12, or 11.2 "Basic" or "Basic Light" zip file: `64-bit
<http://www.oracle.com/technetwork/topics/linuxx86-64soft-092277.html>`__
or `32-bit
<http://www.oracle.com/technetwork/topics/linuxsoft-082809.html>`__, matching your
Python architecture.
2. Unzip the package into a single directory that is accessible to your
application. For example::
mkdir -p /opt/oracle
cd /opt/oracle
unzip instantclient-basic-linux.x64-19.3.0.0.0dbru.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.
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::
sudo sh -c "echo /opt/oracle/instantclient_19_3 > /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_19_3:$LD_LIBRARY_PATH
5. If you intend to co-locate optional Oracle configuration files such
as ``tnsnames.ora``, ``sqlnet.ora`` or ``oraaccess.xml`` with
Instant Client, then put them in the ``network/admin``
subdirectory. With Instant Client 12.2 or earlier, create this
manually. For example::
mkdir -p /opt/oracle/instantclient_12_2/network/admin
This is the default Oracle configuration directory for executables
linked with this Instant Client.
Alternatively, Oracle configuration files can be put in another,
accessible directory. Then set the environment variable
``TNS_ADMIN`` to that directory name.
Oracle Instant Client RPMs
++++++++++++++++++++++++++
To use cx_Oracle with Oracle Instant Client RPMs:
1. Download an Oracle 19, 18, 12, or 11.2 "Basic" or "Basic Light" RPM: `64-bit
<http://www.oracle.com/technetwork/topics/linuxx86-64soft-092277.html>`__
or `32-bit
<http://www.oracle.com/technetwork/topics/linuxsoft-082809.html>`__, matching your
Python architecture.
Oracle's yum server has `Instant Client RPMs for Oracle Linux 7
<http://yum.oracle.com/repo/OracleLinux/OL7/oracle/instantclient/x86_64/index.html>`__
and `Instant Client RPMs for Oracle Linux 6
<http://yum.oracle.com/repo/OracleLinux/OL6/oracle/instantclient/x86_64/index.html>`__
that can be downloaded without needing a click-through.
2. Install the downloaded RPM with sudo or as the root user. For example::
sudo yum install oracle-instantclient19.3-basic-19.3.0.0.0-1.x86_64.rpm
Yum will automatically install required dependencies, such as ``libaio``.
3. For Instant Client 19, 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::
sudo sh -c "echo /usr/lib/oracle/18.3/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.3/client64/lib:$LD_LIBRARY_PATH
4. If you intend to co-locate optional Oracle configuration files such
as ``tnsnames.ora``, ``sqlnet.ora`` or ``oraaccess.xml`` with
Instant Client, then put them in the ``network/admin`` subdirectory
under ``lib/``. With Instant Client 12.2 or earlier, create this
manually. For example::
sudo mkdir -p /usr/lib/oracle/12.2/client64/lib/network/admin
This is the default Oracle configuration directory for executables
linked with this Instant Client.
Alternatively, Oracle configuration files can be put in another,
accessible directory. Then set the environment variable
``TNS_ADMIN`` to that directory name.
Local Database or Full Oracle Client
++++++++++++++++++++++++++++++++++++
cx_Oracle applications can use Oracle Client 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::
source /usr/local/bin/oraenv
For Oracle Database XE, run::
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
<http://yum.oracle.com/>`__. Various versions of Python are easily installed.
Using the yum server makes it easy to keep up to date.
Installation instructions are at `Oracle Linux for Python
Developers <https://yum.oracle.com/oracle-linux-python.html>`__.
Installing cx_Oracle on Windows
===============================
Install cx_Oracle
-----------------
Use Python's `Pip <http://pip.readthedocs.io/en/latest/installing/>`__
package to install cx_Oracle from `PyPI
<https://pypi.python.org/pypi/cx_Oracle>`__::
python -m pip install cx_Oracle --upgrade
This will download and install a pre-compiled binary `if one is
available <https://pypi.python.org/pypi/cx_Oracle>`__ for your
architecture. If a pre-compiled binary is not available, the source
will be downloaded, compiled, and the resulting binary installed.
Install Oracle Client
---------------------
Using cx_Oracle requires Oracle Client libraries to be installed.
These provide the necessary network connectivity allowing cx_Oracle
to access an Oracle Database instance. Oracle Client versions 18,
12 and 11.2 are supported.
- If your database is on a remote computer, then download the free `Oracle
Instant Client
<http://www.oracle.com/technetwork/database/database-technologies/instant-client/overview/index.html>`__
"Basic" or "Basic Light" package for your operating system
architecture.
- Alternatively use the client libraries already available in a
locally installed database such as the free `Oracle XE
<https://www.oracle.com/database/technologies/appdev/xe.html>`__
release.
Oracle Instant Client Zip Files
+++++++++++++++++++++++++++++++
To use cx_Oracle with Oracle Instant Client zip files:
1. Download an Oracle 18, 12, or 11.2 "Basic" or "Basic Light" zip
file: `64-bit
<http://www.oracle.com/technetwork/topics/winx64soft-089540.html>`__
or `32-bit
<http://www.oracle.com/technetwork/topics/winsoft-085727.html>`__, matching your
Python architecture.
2. Unzip the package into a directory that is accessible to your
application. For example unzip
``instantclient-basic-windows.x64-18.3.0.0.0dbru.zip`` to
``C:\oracle\instantclient_18_3``.
3. Add this directory to the ``PATH`` environment variable. For
example, on Windows 7, update ``PATH`` in Control Panel -> System
-> Advanced System Settings -> Advanced -> Environment Variables ->
System Variables -> PATH. The Instant Client directory must occur
in ``PATH`` before any other Oracle directories.
Restart any open command prompt windows.
To avoid interfering with existing tools that require other Oracle
Client versions, instead of updating the system-wide ``PATH`` variable, you
may prefer to write a batch file that sets ``PATH``, for example::
REM mypy.bat
SET PATH=C:\oracle\instantclient_18_3;%PATH%
python %*
Invoke this batch file everytime you want to run python.
Alternatively use ``SET`` to change your ``PATH`` in each command
prompt window before you run python.
4. 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 18 or 12.2 install `VS 2013 <https://support.microsoft.com/en-us/kb/2977003#bookmark-vs2013>`__
- For Instant Client 12.1 install `VS 2010 <https://support.microsoft.com/en-us/kb/2977003#bookmark-vs2010>`__
- For Instant Client 11.2 install `VS 2005 64-bit <https://www.microsoft.com/en-us/download/details.aspx?id=18471>`__ or `VS 2005 32-bit <https://www.microsoft.com/en-ca/download/details.aspx?id=3387>`__
5. If you intend to co-locate optional Oracle configuration files such
as ``tnsnames.ora``, ``sqlnet.ora`` or ``oraaccess.xml`` with
Instant Client, then create a ``network\admin`` subdirectory, for example
``C:\oracle\instantclient_18_3\network\admin``.
This is the default Oracle configuration directory for executables
linked with this Instant Client.
Alternatively, Oracle configuration files can be put in another,
accessible directory. Then set the environment variable
``TNS_ADMIN`` to that directory name.
Local Database or Full Oracle Client
++++++++++++++++++++++++++++++++++++
cx_Oracle applications can use Oracle Client 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. For example, on Windows
7, update ``PATH`` in Control Panel -> System -> Advanced System
Settings -> Advanced -> Environment Variables -> System Variables
-> PATH.
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, Oracle configuration files can be put in another,
accessible directory. Then set the environment variable
``TNS_ADMIN`` to that directory name.
Installing cx_Oracle on macOS
=============================
Install Python
--------------
Make sure you are not using the bundled Python. This has restricted
entitlements and will fail to load Oracle client libraries. Instead
use `Homebrew <https://brew.sh>`__ or `Python.org
<https://www.python.org/downloads>`__.
Install cx_Oracle
-----------------
Use Python's `Pip <http://pip.readthedocs.io/en/latest/installing/>`__
package to install cx_Oracle from `PyPI
<https://pypi.python.org/pypi/cx_Oracle>`__::
python -m pip install cx_Oracle --upgrade
The source will be downloaded, compiled, and the resulting binary
installed.
Install Oracle Instant Client
-----------------------------
cx_Oracle requires Oracle Client libraries, which are found in Oracle
Instant Client for macOS. These provide the necessary network
connectivity allowing cx_Oracle to access an Oracle Database
instance. Oracle Client versions 18, 12 and 11.2 are supported.
To use cx_Oracle with Oracle Instant Client zip files:
1. Download the Oracle 18, 12 or 11.2 "Basic" or "Basic Light" zip file from `here
<http://www.oracle.com/technetwork/topics/intel-macsoft-096467.html>`__.
Choose either a 64-bit or 32-bit package, matching your
Python architecture.
2. Unzip the package into a single directory that is accessible to your
application. For example::
mkdir -p /opt/oracle
unzip instantclient-basic-macos.x64-18.1.0.0.0.zip
3. Add links to ``$HOME/lib`` or ``/usr/local/lib`` to enable
applications to find the library. For example::
mkdir ~/lib
ln -s /opt/oracle/instantclient_18_1/libclntsh.dylib ~/lib/
Alternatively, copy the required OCI libraries. For example::
mkdir ~/lib
cp /opt/oracle/instantclient_18_1/{libclntsh.dylib.18.1,libclntshcore.dylib.18.1,libons.dylib,libnnz18.dylib,libociei.dylib} ~/lib/
For Instant Client 11.2, the OCI libraries must be copied. For example::
mkdir ~/lib
cp /opt/oracle/instantclient_11_2/{libclntsh.dylib.11.1,libnnz11.dylib,libociei.dylib} ~/lib/
4. If you intend to co-locate optional Oracle configuration files such
as ``tnsnames.ora``, ``sqlnet.ora`` or ``oraaccess.xml`` with
Instant Client, then create a ``network/admin`` subdirectory, if it
does not already exist. For example::
mkdir -p /opt/oracle/instantclient_12_2/network/admin
This is the default Oracle configuration directory for executables
linked with this Instant Client.
Alternatively, Oracle configuration files can be put in another,
accessible directory. Then set the environment variable
``TNS_ADMIN`` to that directory name.
Installing cx_Oracle without Internet Access
============================================
To install cx_Oracle on a computer that is not connected to the
internet, download the appropriate cx_Oracle file from `PyPI
<https://pypi.org/project/cx-Oracle/#files>`__. Transfer this file to
the offline computer and install it with::
python -m pip install "<file_name>"
Then follow the general cx_Oracle platform installation instructions
to install Oracle client libraries.
Install Using GitHub
====================
In order to install using the source on GitHub, use the following commands::
git clone https://github.com/oracle/python-cx_Oracle.git cx_Oracle
cd cx_Oracle
git submodule init
git submodule update
python setup.py install
Note that if you download a source zip file directly from GitHub then
you will also need to download an `ODPI-C
<https://github.com/oracle/odpi>`__ source zip file and extract it
inside the directory called "odpi".
cx_Oracle source code is also available from oss.oracle.com. This can
be cloned with::
git clone git://oss.oracle.com/git/oracle/python-cx_Oracle.git cx_Oracle
cd cx_Oracle
git submodule init
git submodule update
Install Using Source from PyPI
==============================
The source package can be downloaded manually from
`PyPI <https://pypi.python.org/pypi/cx_Oracle>`__ and extracted, after
which the following commands should be run::
python setup.py build
python setup.py install
Upgrading from cx_Oracle 6
==========================
Review the `release notes
<http://cx-oracle.readthedocs.io/en/latest/releasenotes.html>`__ for
deprecations and modify any affected code.
Upgrading from cx_Oracle 5
==========================
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 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`.
Installing cx_Oracle 5.3
========================
If you require cx_Oracle 5.3, download a Windows installer from `PyPI
<https://pypi.python.org/pypi/cx_Oracle>`__ or use ``python -m pip
install cx-oracle==5.3`` to install from source.
Very old versions of cx_Oracle can be found in the files section at
`SourceForce <https://sourceforge.net/projects/cx-oracle/files/>`__.
Troubleshooting
===============
If installation fails:
- Use option ``-v`` with pip. Review your output and logs. Try to install
using a different method. **Google anything that looks like an error.**
Try some potential solutions.
- Was there a network connection error? Do you need to see the environment
variables ``http_proxy`` and/or ``https_proxy``?
- Do you get the error "``No module named pip``"? The pip module is builtin
to Python from version 2.7.9 but is sometimes removed by the OS. Use the
venv module (builtin to Python 3.x) or virtualenv module (Python 2.x)
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``"?
- Check that Python, cx_Oracle and your Oracle Client libraries
are all 64-bit or all 32-bit. The ``DPI-1047`` message will
tell you whether the 64-bit or 32-bit Oracle Client is needed
for your Python.
- On Windows, 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 on the directory set in
``PATH``. Verify that ``OCI.DLL`` exists there.
- On Windows, check that the correct `Windows Redistributables
<https://oracle.github.io/odpi/doc/installation.html#windows>`__ have
been installed.
- On Linux, check the ``LD_LIBRARY_PATH`` environment variable
contains the Oracle Client library directory.
- On macOS, make sure Oracle Instant Client is in ``~/lib`` or
``/usr/local/lib`` and that you are not using the bundled Python (use
`Homebrew <https://brew.sh>`__ or `Python.org
<https://www.python.org/downloads>`__ instead).
- If you have both Python 2 and 3 installed, make sure you are
using the correct python and pip (or python3 and pip3)
executables.

View File

@ -1,3 +1,5 @@
:orphan:
.. _license:
*******
@ -8,7 +10,7 @@ License
.. centered:: **LICENSE AGREEMENT FOR CX_ORACLE**
Copyright |copy| 2016, 2018, Oracle and/or its affiliates. All rights reserved.
Copyright |copy| 2016, 2020, Oracle and/or its affiliates. All rights reserved.
Copyright |copy| 2007-2015, Anthony Tuininga. All rights reserved.

View File

@ -1,12 +1,338 @@
:orphan:
.. _releasenotes:
cx_Oracle Release Notes
=======================
7.x releases
############
.. note::
**cx_Oracle has a major new release under a new name and homepage**
`python-oracledb <https://oracle.github.io/python-oracledb/>`__.
**New projects should install python-oracledb instead of cx_Oracle.**
For any deprecations, see :ref:`Deprecations <deprecations>`.
Version 8.3 (November 2021)
---------------------------
#) Updated embedded ODPI-C to `version 4.3.0
<https://oracle.github.io/odpi/doc/releasenotes.html#
version-4-3-november-4-2021>`__.
#) 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
<https://oracle.github.io/odpi/doc/releasenotes.html#
version-4-2-1-june-1-2021>`__.
#) 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
<https://oracle.github.io/odpi/doc/releasenotes.html#
version-4-2-may-18-2021>`__.
#) 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 <sodametadatacache>` 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 <https://github.com/oracle/python-cx_Oracle/issues/385>`__).
Initial work was done in `PR 549
<https://github.com/oracle/python-cx_Oracle/pull/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
<https://github.com/oracle/python-cx_Oracle/issues/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
<https://oracle.github.io/odpi/doc/releasenotes.html#
version-4-1-december-8-2020>`__.
#) 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 <https://github.com/oracle/python-cx_Oracle/pull/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
<https://oracle.github.io/odpi/doc/releasenotes.html#
version-4-0-2-august-31-2020>`__. This includes the fix for binding and
fetching numbers with 39 or 40 decimal digits
(`issue 459 <https://github.com/oracle/python-cx_Oracle/issues/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
<https://oracle.github.io/odpi/doc/releasenotes.html#
version-4-0-1-june-26-2020>`__.
#) Reworked type management to clarify and simplify code
- Added :ref:`constants <dbtypes>` 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 <dbtypesynonyms>` 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 <types>` 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 <objecttype>` 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 <https://github.com/oracle/python-cx_Oracle/issues/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 <https://github.com/oracle/python-cx_Oracle/pull/419>`__,
`420 <https://github.com/oracle/python-cx_Oracle/pull/420>`__,
`421 <https://github.com/oracle/python-cx_Oracle/pull/421>`__,
`422 <https://github.com/oracle/python-cx_Oracle/pull/422>`__,
`423 <https://github.com/oracle/python-cx_Oracle/pull/423>`__,
`437 <https://github.com/oracle/python-cx_Oracle/pull/437>`__ and
`438 <https://github.com/oracle/python-cx_Oracle/pull/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 <https://github.com/oracle/python-cx_Oracle/pull/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
<https://oracle.github.io/odpi/doc/releasenotes.html#
version-3-3-december-2-2019>`__.
#) 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 <Cursor.lastrowid>` 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 <cx_Oracle.SessionPool>`.
#) 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
<https://github.com/oracle/node-oracledb/issues/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 <Connection.msgproperties()>`.
#) 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
<https://oracle.github.io/odpi/doc/releasenotes.html#
version-3-2-2-october-1-2019>`__.
#) 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 <https://github.com/oracle/odpi/issues/112>`__).
#) Improved documentation.
Version 7.2.2 (August 2019)
---------------------------
#) Updated embedded ODPI-C to `version 3.2.1
<https://oracle.github.io/odpi/doc/releasenotes.html#
version-3-2-1-august-12-2019>`__.
#) 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 <https://github.com/oracle/python-cx_Oracle/issues/330>`__).
#) Improved test suite and samples.
#) Improved documentation.
.. _releasenotes70:
Version 7.2 (July 2019)
-----------------------
@ -36,10 +362,10 @@ Version 7.2 (July 2019)
(`issue 299 <https://github.com/oracle/python-cx_Oracle/issues/299>`__).
#) Added mode :data:`cx_Oracle.DEFAULT_AUTH` as requested
(`issue 293 <https://github.com/oracle/python-cx_Oracle/issues/293>`__).
#) Added support for setting the LOB prefetch length indicator in order to
#) 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()`.
: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
@ -213,7 +539,7 @@ Version 7.0 (September 2018)
:ref:`SODA Collection <sodacoll>` and :ref:`SODA Document <sodadoc>` for
more information.
#) Added support for call timeouts available in Oracle Client 18.1 and
higher. See :attr:`Connection.callTimeout`.
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()`.
@ -247,12 +573,6 @@ Version 7.0 (September 2018)
#) Improved documentation.
6.x releases
############
.. _releasenotes60:
Version 6.4.1 (July 2018)
-------------------------
@ -349,7 +669,7 @@ Version 6.3.1 (May 2018)
- 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 statments are reset.
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
@ -548,7 +868,7 @@ Version 6.0.3 (November 2017)
<https://oracle.github.io/odpi/doc/releasenotes.html#
version-2-0-3-november-6-2017>`__.
- Prevent use of unitialized data in certain cases (`issue 77
- Prevent use of uninitialized data in certain cases (`issue 77
<https://github.com/oracle/python-cx_Oracle/issues/77>`__).
- Attempting to ping a database earlier than 10g results in error
"ORA-1010: invalid OCI operation", but that implies a response from the
@ -620,9 +940,6 @@ Version 6.0.1 (August 2017)
Version 6.0 (August 2017)
-------------------------
See :ref:`What's New <whatsnew60>` for a summary of the changes between
cx_Oracle 5.3 and cx_Oracle 6.0.
#) Update to `ODPI-C 2.0 <https://oracle.github.io/odpi/doc/releasenotes.html
#version-2-0-august-14-2017>`__.
@ -767,11 +1084,11 @@ Version 6.0 beta 1 (April 2017)
tagging.
#) Added parameter edition to the :meth:`cx_Oracle.SessionPool` method.
#) Added support for
`universal rowids <https://github.com/oracle/python-cx_Oracle/blob/master/
samples/UniversalRowids.py>`__.
`universal rowids <https://github.com/oracle/python-cx_Oracle/blob/main/
samples/universal_rowids.py>`__.
#) Added support for `DML Returning of multiple rows
<https://github.com/oracle/python-cx_Oracle/blob/master/samples/
DMLReturningMultipleRows.py>`__.
<https://github.com/oracle/python-cx_Oracle/blob/main/samples/
dml_returning_multiple_rows.py>`__.
#) Added attributes :attr:`Variable.actualElements` and
:attr:`Variable.values` to variables.
#) Added parameters region, sharding_key and super_sharding_key to the
@ -800,23 +1117,19 @@ Version 6.0 beta 1 (April 2017)
#) 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 <https://github.com/oracle/
python-cx_Oracle/blob/master/samples/AppContext.py>`__.
python-cx_Oracle/blob/main/samples/app_context.py>`__.
#) Dropped deprecated attribute numbersAsString from
:ref:`cursor objects <cursorobj>`. Use an output type handler instead as
shown in this `sample <https://github.com/oracle/python-cx_Oracle/blob/
master/samples/ReturnNumbersAsDecimals.py>`__.
main/samples/return_numbers_as_decimals.py>`__.
#) Dropped deprecated attributes cqqos and rowids from
:ref:`subscription objects <subscrobj>`. Use the qos attribute instead as
shown in this `sample <https://github.com/oracle/python-cx_Oracle/blob/
master/samples/CQN.py>`__.
main/samples/cqn.py>`__.
#) Dropped deprecated parameters cqqos and rowids from the
:meth:`Connection.subscribe()` method. Use the qos parameter instead as
shown in this `sample <https://github.com/oracle/python-cx_Oracle/blob/
master/samples/CQN.py>`__.
5.x releases
############
main/samples/cqn.py>`__.
Version 5.3 (March 2017)
@ -1132,9 +1445,6 @@ Version 5.0 (December 2008)
session pools to fetch objects without exceptions taking place.
Older releases
##############
Version 4.4.1 (October 2008)
----------------------------
@ -1252,7 +1562,7 @@ Version 4.3.2 (August 2007)
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 tranasction handle if
#) 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.
@ -1399,7 +1709,7 @@ Version 4.1.1 (December 2005)
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 alos be linked against. Thanks to Bernard Delmee for the
ttsh10 must also be linked against. Thanks to Bernard Delmee for the
information.
@ -1767,4 +2077,3 @@ Version 2.2 (July 2001)
installation as suggested by Steve Holden.
#) Added simple usage example as requested by many people.
#) Added HISTORY file to the distribution.

218
doc/src/user_guide/aq.rst Normal file
View File

@ -0,0 +1,218 @@
.. _aqusermanual:
****************************
Oracle Advanced Queuing (AQ)
****************************
.. note::
**cx_Oracle has a major new release under a new name and homepage**
`python-oracledb <https://oracle.github.io/python-oracledb/>`__.
**New projects should install python-oracledb instead of cx_Oracle.**
`Oracle Advanced Queuing
<https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=ADQUE>`__ is a highly
configurable and scalable messaging feature of Oracle Database. It has
interfaces in various languages, letting you integrate multiple tools in your
architecture.
cx_Oracle 7.2 introduced an updated interface for Oracle Advanced
Queuing.
There are Advanced Queuing examples in the `GitHub examples
<https://github.com/oracle/python-cx_Oracle/tree/main/samples>`__ directory.
Creating a Queue
================
Before being used, queues need to be created in the database, for example in
SQL*Plus:
.. code-block:: sql
begin
dbms_aqadm.create_queue_table('MY_QUEUE_TABLE', 'RAW');
dbms_aqadm.create_queue('DEMO_RAW_QUEUE', 'MY_QUEUE_TABLE');
dbms_aqadm.start_queue('DEMO_RAW_QUEUE');
end;
/
This examples creates a RAW queue suitable for sending string or raw bytes
messages.
Enqueuing Messages
==================
To send messages in Python you connect and get a :ref:`queue <queue>`. The
queue can be used for enqueuing, dequeuing, or both as needed.
.. code-block:: python
queue = connection.queue("DEMO_RAW_QUEUE")
Now messages can be queued using :meth:`~Queue.enqone()`. To send three
messages:
.. code-block:: python
PAYLOAD_DATA = [
"The first message",
"The second message",
"The third message"
]
for data in PAYLOAD_DATA:
queue.enqone(connection.msgproperties(payload=data))
connection.commit()
Since the queue sending the messages is a RAW queue, the strings in this
example will be internally encoded to bytes using :attr:`Connection.encoding`
before being enqueued.
Dequeuing Messages
==================
Dequeuing is performed similarly. To dequeue a message call the method
:meth:`~Queue.deqone()` as shown. Note that if the message is expected to be a
string, the bytes must be decoded using :attr:`Connection.encoding`.
.. code-block:: python
queue = connection.queue("DEMO_RAW_QUEUE")
msg = queue.deqOne()
connection.commit()
print(msg.payload.decode(connection.encoding))
Using Object Queues
===================
Named Oracle objects can be enqueued and dequeued as well. Given an object
type called ``UDT_BOOK``:
.. code-block:: sql
CREATE OR REPLACE TYPE udt_book AS OBJECT (
Title VARCHAR2(100),
Authors VARCHAR2(100),
Price NUMBER(5,2)
);
/
And a queue that accepts this type:
.. code-block:: sql
begin
dbms_aqadm.create_queue_table('BOOK_QUEUE_TAB', 'UDT_BOOK');
dbms_aqadm.create_queue('DEMO_BOOK_QUEUE', 'BOOK_QUEUE_TAB');
dbms_aqadm.start_queue('DEMO_BOOK_QUEUE');
end;
/
You can queue messages:
.. code-block:: python
book_type = connection.gettype("UDT_BOOK")
queue = connection.queue("DEMO_BOOK_QUEUE", book_type)
book = book_type.newobject()
book.TITLE = "Quick Brown Fox"
book.AUTHORS = "The Dog"
book.PRICE = 123
queue.enqone(connection.msgproperties(payload=book))
connection.commit()
Dequeuing is done like this:
.. code-block:: python
book_type = connection.gettype("UDT_BOOK")
queue = connection.queue("DEMO_BOOK_QUEUE", book_type)
msg = queue.deqone()
connection.commit()
print(msg.payload.TITLE) # will print Quick Brown Fox
Changing Queue and Message Options
==================================
Refer to the :ref:`cx_Oracle AQ API <aq>` and
`Oracle Advanced Queuing documentation
<https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=ADQUE>`__ for details
on all of the enqueue and dequeue options available.
Enqueue options can be set. For example, to make it so that an explicit
call to :meth:`~Connection.commit()` on the connection is not needed to commit
messages:
.. code-block:: python
queue = connection.queue("DEMO_RAW_QUEUE")
queue.enqoptions.visibility = cx_Oracle.ENQ_IMMEDIATE
Dequeue options can also be set. For example, to specify not to block on
dequeuing if no messages are available:
.. code-block:: python
queue = connection.queue("DEMO_RAW_QUEUE")
queue.deqoptions.wait = cx_Oracle.DEQ_NO_WAIT
Message properties can be set when enqueuing. For example, to set an
expiration of 60 seconds on a message:
.. code-block:: python
queue.enqone(connection.msgproperties(payload="Message", expiration=60))
This means that if no dequeue operation occurs within 60 seconds that the
message will be dropped from the queue.
Bulk Enqueue and Dequeue
========================
The :meth:`~Queue.enqmany()` and :meth:`~Queue.deqmany()` methods can be used
for efficient bulk message handling.
:meth:`~Queue.enqmany()` is similar to :meth:`~Queue.enqone()` but accepts an
array of messages:
.. code-block:: python
messages = [
"The first message",
"The second message",
"The third message",
]
queue = connection.queue("DEMO_RAW_QUEUE")
queue.enqmany(connection.msgproperties(payload=m) for m in messages)
connection.commit()
.. warning::
Calling :meth:`~Queue.enqmany()` in parallel on different connections
acquired from the same pool may fail due to Oracle bug 29928074. Ensure
that this function is not run in parallel, use standalone connections or
connections from different pools, or make multiple calls to
:meth:`~Queue.enqone()` instead. The function :meth:`~Queue.deqmany()` call
is not affected.
To dequeue multiple messages at one time, use :meth:`~Queue.deqmany()`. This
takes an argument specifying the maximum number of messages to dequeue at one
time:
.. code-block:: python
for m in queue.deqmany(10):
print(m.payload.decode(connection.encoding))
Depending on the queue properties and the number of messages available to
dequeue, this code will print out from zero to ten messages.

View File

@ -0,0 +1,301 @@
.. _batchstmnt:
******************************************
Batch Statement Execution and Bulk Loading
******************************************
.. note::
**cx_Oracle has a major new release under a new name and homepage**
`python-oracledb <https://oracle.github.io/python-oracledb/>`__.
**New projects should install python-oracledb instead of cx_Oracle.**
Inserting or updating multiple rows can be performed efficiently with
:meth:`Cursor.executemany()`, making it easy to work with large data sets with
cx_Oracle. This method can significantly outperform repeated calls to
:meth:`Cursor.execute()` by reducing network transfer costs and database overheads.
The :meth:`~Cursor.executemany()` method can also be used to execute PL/SQL
statements multiple times at once.
There are examples in the `GitHub examples
<https://github.com/oracle/python-cx_Oracle/tree/main/samples>`__
directory.
The following tables will be used in the samples that follow:
.. code-block:: sql
create table ParentTable (
ParentId number(9) not null,
Description varchar2(60) not null,
constraint ParentTable_pk primary key (ParentId)
);
create table ChildTable (
ChildId number(9) not null,
ParentId number(9) not null,
Description varchar2(60) not null,
constraint ChildTable_pk primary key (ChildId),
constraint ChildTable_fk foreign key (ParentId)
references ParentTable
);
Batch Execution of SQL
======================
The following example inserts five rows into the table ``ParentTable``:
.. code-block:: python
data = [
(10, 'Parent 10'),
(20, 'Parent 20'),
(30, 'Parent 30'),
(40, 'Parent 40'),
(50, 'Parent 50')
]
cursor.executemany("insert into ParentTable values (:1, :2)", data)
This code requires only one :ref:`round-trip <roundtrips>` from the client to
the database instead of the five round-trips that would be required for
repeated calls to :meth:`~Cursor.execute()`. For very large data sets there
may be an external buffer or network limits to how many rows can be processed,
so repeated calls to ``executemany()`` may be required. The limits are based
on both the number of rows being processed as well as the "size" of each row
that is being processed. Repeated calls to :meth:`~Cursor.executemany()` are
still better than repeated calls to :meth:`~Cursor.execute()`.
Batch Execution of PL/SQL
=========================
PL/SQL functions and procedures and anonymous PL/SQL blocks can also be called
using :meth:`~Cursor.executemany()` in order to improve performance. For
example:
.. code-block:: python
data = [
(10, 'Parent 10'),
(20, 'Parent 20'),
(30, 'Parent 30'),
(40, 'Parent 40'),
(50, 'Parent 50')
]
cursor.executemany("begin mypkg.create_parent(:1, :2); end;", data)
Note that the ``batcherrors`` parameter (discussed below) cannot be used with
PL/SQL block execution.
Handling Data Errors
====================
Large datasets may contain some invalid data. When using batch execution as
discussed above, the entire batch will be discarded if a single error is
detected, potentially eliminating the performance benefits of batch execution
and increasing the complexity of the code required to handle those errors. If
the parameter ``batchErrors`` is set to the value ``True`` when calling
:meth:`~Cursor.executemany()`, however, processing will continue even if there
are data errors in some rows, and the rows containing errors can be examined
afterwards to determine what course the application should take. Note that if
any errors are detected, a transaction will be started but not committed, even
if :attr:`Connection.autocommit` is set to ``True``. After examining the errors
and deciding what to do with them, the application needs to explicitly commit
or roll back the transaction with :meth:`Connection.commit()` or
:meth:`Connection.rollback()`, as needed.
This example shows how data errors can be identified:
.. code-block:: python
data = [
(60, 'Parent 60'),
(70, 'Parent 70'),
(70, 'Parent 70 (duplicate)'),
(80, 'Parent 80'),
(80, 'Parent 80 (duplicate)'),
(90, 'Parent 90')
]
cursor.executemany("insert into ParentTable values (:1, :2)", data,
batcherrors=True)
for error in cursor.getbatcherrors():
print("Error", error.message, "at row offset", error.offset)
The output is::
Error ORA-00001: unique constraint (PYTHONDEMO.PARENTTABLE_PK) violated at row offset 2
Error ORA-00001: unique constraint (PYTHONDEMO.PARENTTABLE_PK) violated at row offset 4
The row offset is the index into the array of the data that could not be
inserted due to errors. The application could choose to commit or rollback the
other rows that were successfully inserted. Alternatively, it could correct
the data for the two invalid rows and attempt to insert them again before
committing.
Identifying Affected Rows
=========================
When executing a DML statement using :meth:`~Cursor.execute()`, the number of
rows affected can be examined by looking at the attribute
:attr:`~Cursor.rowcount`. When performing batch executing with
:meth:`Cursor.executemany()`, however, the row count will return the *total*
number of rows that were affected. If you want to know the total number of rows
affected by each row of data that is bound you must set the parameter
``arraydmlrowcounts`` to ``True``, as shown:
.. code-block:: python
parent_ids_to_delete = [20, 30, 50]
cursor.executemany("delete from ChildTable where ParentId = :1",
[(i,) for i in parent_ids_to_delete],
arraydmlrowcounts=True)
row_counts = cursor.getarraydmlrowcounts()
for parent_id, count in zip(parent_ids_to_delete, row_counts):
print("Parent ID:", parent_id, "deleted", count, "rows.")
Using the data found in the `GitHub samples
<https://github.com/oracle/python-cx_Oracle/tree/main/samples>`__ the output
is as follows::
Parent ID: 20 deleted 3 rows.
Parent ID: 30 deleted 2 rows.
Parent ID: 50 deleted 4 rows.
DML RETURNING
=============
DML statements like INSERT, UPDATE, DELETE and MERGE can return values by using
the DML RETURNING syntax. A bind variable can be created to accept this data.
See :ref:`bind` for more information.
If, instead of merely deleting the rows as shown in the previous example, you
also wanted to know some information about each of the rows that were deleted,
you could use the following code:
.. code-block:: python
parent_ids_to_delete = [20, 30, 50]
child_id_var = cursor.var(int, arraysize=len(parent_ids_to_delete))
cursor.setinputsizes(None, child_id_var)
cursor.executemany("""
delete from ChildTable
where ParentId = :1
returning ChildId into :2""",
[(i,) for i in parent_ids_to_delete])
for ix, parent_id in enumerate(parent_ids_to_delete):
print("Child IDs deleted for parent ID", parent_id, "are",
child_id_var.getvalue(ix))
The output would then be::
Child IDs deleted for parent ID 20 are [1002, 1003, 1004]
Child IDs deleted for parent ID 30 are [1005, 1006]
Child IDs deleted for parent ID 50 are [1012, 1013, 1014, 1015]
Note that the bind variable created to accept the returned data must have an
arraysize large enough to hold data for each row that is processed. Also,
the call to :meth:`Cursor.setinputsizes()` binds this variable immediately so
that it does not have to be passed in each row of data.
Predefining Memory Areas
========================
When multiple rows of data are being processed there is the possibility that
the data is not uniform in type and size. In such cases, cx_Oracle makes some
effort to accommodate such differences. Type determination for each column is
deferred until a value that is not ``None`` is found in the column's data. If
all values in a particular column are ``None``, then cx_Oracle assumes the type
is a string and has a length of 1. cx_Oracle will also adjust the size of the
buffers used to store strings and bytes when a longer value is encountered in
the data. These sorts of operations incur overhead as memory has to be
reallocated and data copied. To eliminate this overhead, using
:meth:`~Cursor.setinputsizes()` tells cx_Oracle about the type and size of the
data that is going to be used.
Consider the following code:
.. code-block:: python
data = [
(110, "Parent 110"),
(2000, "Parent 2000"),
(30000, "Parent 30000"),
(400000, "Parent 400000"),
(5000000, "Parent 5000000")
]
cursor.setinputsizes(None, 20)
cursor.executemany("""
insert into ParentTable (ParentId, Description)
values (:1, :2)""", data)
In this example, without the call to :meth:`~Cursor.setinputsizes()`, cx_Oracle
would perform five allocations of increasing size as it discovered each new,
longer string. However ``cursor.setinputsizes(None, 20)`` tells cx_Oracle that
the maximum size of the strings that will be processed is 20 characters. Since
cx_Oracle allocates memory for each row based on this value, it is best not to
oversize it. The first parameter of ``None`` tells cx_Oracle that its default
processing will be sufficient.
Loading CSV Files into Oracle Database
======================================
The :meth:`Cursor.executemany()` method and Python's `csv module
<https://docs.python.org/3/library/csv.html#module-csv>`__ can be used to
efficiently load CSV (Comma Separated Values) files. For example, consider the
file ``data.csv``::
101,Abel
154,Baker
132,Charlie
199,Delta
. . .
And the schema:
.. code-block:: sql
create table test (id number, name varchar2(25));
Data loading can be done in batches of records since the number of records may
prevent all data being inserted at once:
.. code-block:: python
import cx_Oracle
import csv
# Predefine the memory areas to match the table definition.
# This can improve performance by avoiding memory reallocations.
# Here, one parameter is passed for each of the columns.
# "None" is used for the ID column, since the size of NUMBER isn't
# variable. The "25" matches the maximum expected data size for the
# NAME column
cursor.setinputsizes(None, 25)
# Adjust the number of rows to be inserted in each iteration
# to meet your memory and performance requirements
batch_size = 10000
with open('testsp.csv', 'r') as csv_file:
csv_reader = csv.reader(csv_file, delimiter=',')
sql = "insert into test (id,name) values (:1, :2)"
data = []
for line in csv_reader:
data.append((line[0], line[1]))
if len(data) % batch_size == 0:
cursor.executemany(sql, data)
data = []
if data:
cursor.executemany(sql, data)
con.commit()
Depending on data sizes and business requirements, database changes such as
temporarily disabling redo logging on the table, or disabling indexes may also
be beneficial.

812
doc/src/user_guide/bind.rst Normal file
View File

@ -0,0 +1,812 @@
.. _bind:
********************
Using Bind Variables
********************
.. note::
**cx_Oracle has a major new release under a new name and homepage**
`python-oracledb <https://oracle.github.io/python-oracledb/>`__.
**New projects should install python-oracledb instead of cx_Oracle.**
SQL and PL/SQL statements that pass data to and from Oracle Database should use
placeholders in SQL and PL/SQL statements that mark where data is supplied or
returned. These placeholders are referred to as bind variables or bind
parameters A bind variable is a colon-prefixed identifier or numeral. For
example, there are two bind variables (``dept_id`` and ``dept_name``) in this
SQL statement:
.. code-block:: python
sql = """insert into departments (department_id, department_name)
values (:dept_id, :dept_name)"""
cursor.execute(sql, [280, "Facility"])
Using bind variables is important for scalability and security. They help avoid
SQL Injection security problems because data is never treated as part of an
executable statement. Never concatenate or interpolate user data into SQL
statements:
.. code-block:: python
did = 280
dnm = "Facility"
# !! Never do this !!
sql = f"""insert into departments (department_id, department_name)
values ({did}, '{dnm}')"""
cursor.execute(sql)
Bind variables reduce parsing and execution costs when statements are executed
more than once with different data values. If you do not use bind variables,
Oracle must reparse and cache multiple statements. When using bind variables,
Oracle Database may be able to reuse the statement execution plan and context.
Bind variables can be used to substitute data, but cannot be used to substitute
the text of the statement. You cannot, for example, use a bind variable where
a column name or a table name is required. Bind variables also cannot be used
in Data Definition Language (DDL) statements, such as CREATE TABLE or ALTER
statements.
Binding By Name or Position
===========================
Binding can be done by name or by position. A named bind is performed when the
bind variables in a statement are associated with a name. For example:
.. code-block:: python
cursor.execute("""
insert into departments (department_id, department_name)
values (:dept_id, :dept_name)""", dept_id=280,
dept_name="Facility")
# alternatively, the parameters can be passed as a dictionary instead of as
# keyword parameters
data = dict(dept_id=280, dept_name="Facility")
cursor.execute("""
insert into departments (department_id, department_name)
values (:dept_id, :dept_name)""", data)
In the above example, the keyword parameter names or the keys of the dictionary
must match the bind variable names. The advantages of this approach are that
the location of the bind variables in the statement is not important, the
names can be meaningful and the names can be repeated while still only
supplying the value once.
A positional bind is performed when a list of bind values are passed to the
execute() call. For example:
.. code-block:: python
cursor.execute("""
insert into departments (department_id, department_name)
values (:dept_id, :dept_name)""", [280, "Facility"])
Note that for SQL statements, the order of the bind values must exactly match
the order of each bind variable and duplicated names must have their values
repeated. For PL/SQL statements, however, the order of the bind values must
exactly match the order of each **unique** bind variable found in the PL/SQL
block and values should not be repeated. In order to avoid this difference,
binding by name is recommended when bind variable names are repeated.
Bind Direction
==============
The caller can supply data to the database (IN), the database can return
data to the caller (OUT) or the caller can supply initial data to the
database and the database can supply the modified data back to the caller
(IN/OUT). This is known as the bind direction.
The examples shown above have all supplied data to the database and are
therefore classified as IN bind variables. In order to have the database return
data to the caller, a variable must be created. This is done by calling the
method :func:`Cursor.var()`, which identifies the type of data that will be
found in that bind variable and its maximum size among other things.
Here is an example showing how to use OUT binds. It calculates the sum of the
integers 8 and 7 and stores the result in an OUT bind variable of type integer:
.. code-block:: python
out_val = cursor.var(int)
cursor.execute("""
begin
:out_val := :in_bind_var1 + :in_bind_var2;
end;""",
out_val=out_val, in_bind_var1=8, in_bind_var2=7)
print(out_val.getvalue()) # will print 15
If instead of simply getting data back you wish to supply an initial value to
the database, you can set the variable's initial value. This example is the
same as the previous one but it sets the initial value first:
.. code-block:: python
in_out_var = cursor.var(int)
in_out_var.setvalue(0, 25)
cursor.execute("""
begin
:in_out_bind_var := :in_out_bind_var + :in_bind_var1 +
:in_bind_var2;
end;""",
in_out_bind_var=in_out_var, in_bind_var1=8, in_bind_var2=7)
print(in_out_var.getvalue()) # will print 40
When binding data to parameters of PL/SQL procedures that are declared as OUT
parameters, it is worth noting that any value that is set in the bind variable
will be ignored. In addition, any parameters declared as IN/OUT that do not
have a value set will start out with a value of ``null``.
Binding Null Values
===================
In cx_Oracle, null values are represented by the Python singleton ``None``.
For example:
.. code-block:: python
cursor.execute("""
insert into departments (department_id, department_name)
values (:dept_id, :dept_name)""", dept_id=280, dept_name=None)
In this specific case, because the ``DEPARTMENT_NAME`` column is defined as a
``NOT NULL`` column, an error will occur::
cx_Oracle.IntegrityError: ORA-01400: cannot insert NULL into ("HR"."DEPARTMENTS"."DEPARTMENT_NAME")
If this value is bound directly, cx_Oracle assumes it to be a string
(equivalent to a VARCHAR2 column). If you need to use a different Oracle type
you will need to make a call to :func:`Cursor.setinputsizes()` or create a bind
variable with the correct type by calling :func:`Cursor.var()`.
Binding ROWID Values
====================
The pseudo-column ``ROWID`` uniquely identifies a row within a table. In
cx_Oracle, ROWID values are represented as strings. The example below shows
fetching a row and then updating that row by binding its rowid:
.. code-block:: python
# fetch the row
cursor.execute("""
select rowid, manager_id
from departments
where department_id = :dept_id""", dept_id=280)
rowid, manager_id = cursor.fetchone()
# update the row by binding ROWID
cursor.execute("""
update departments set
manager_id = :manager_id
where rowid = :rid""", manager_id=205, rid=rowid)
DML RETURNING Bind Variables
============================
When a RETURNING clause is used with a DML statement like UPDATE,
INSERT, or DELETE, the values are returned to the application through
the use of OUT bind variables. Consider the following example:
.. code-block:: python
# The RETURNING INTO bind variable is a string
dept_name = cursor.var(str)
cursor.execute("""
update departments set
location_id = :loc_id
where department_id = :dept_id
returning department_name into :dept_name""",
loc_id=1700, dept_id=50, dept_name=dept_name)
print(dept_name.getvalue()) # will print ['Shipping']
In the above example, since the WHERE clause matches only one row, the output
contains a single item in the list. If the WHERE clause matched multiple rows,
however, the output would contain as many items as there were rows that were
updated.
No duplicate binds are allowed in a DML statement with a RETURNING clause, and
no duplication is allowed between bind variables in the DML section and the
RETURNING section of the statement.
LOB Bind Variables
==================
Database CLOBs, NCLOBS, BLOBs and BFILEs can be bound with types
:attr:`cx_Oracle.DB_TYPE_CLOB`, :attr:`cx_Oracle.DB_TYPE_NCLOB`,
:attr:`cx_Oracle.DB_TYPE_BLOB` and :attr:`cx_Oracle.DB_TYPE_BFILE`
respectively. LOBs fetched from the database or created with
:meth:`Connection.createlob()` can also be bound.
LOBs may represent Oracle Database persistent LOBs (those stored in tables) or
temporary LOBs (such as those created with :meth:`Connection.createlob()` or
returned by some SQL and PL/SQL operations).
LOBs can be used as IN, OUT or IN/OUT bind variables.
See :ref:`lobdata` for examples.
.. _refcur:
REF CURSOR Bind Variables
=========================
cx_Oracle provides the ability to bind and define PL/SQL REF cursors. As an
example, consider the PL/SQL procedure:
.. code-block:: sql
CREATE OR REPLACE PROCEDURE find_employees (
p_query IN VARCHAR2,
p_results OUT SYS_REFCURSOR
) AS
BEGIN
OPEN p_results FOR
SELECT employee_id, first_name, last_name
FROM employees
WHERE UPPER(first_name || ' ' || last_name || ' ' || email)
LIKE '%' || UPPER(p_query) || '%';
END;
/
A newly opened cursor can be bound to the REF CURSOR parameter, as shown in the
following Python code. After the PL/SQL procedure has been called with
:meth:`Cursor.callproc()`, the cursor can then be fetched just like any other
cursor which had executed a SQL query:
.. code-block:: python
ref_cursor = connection.cursor()
cursor.callproc("find_employees", ['Smith', ref_cursor])
for row in ref_cursor:
print(row)
With Oracle's `sample HR schema
<https://github.com/oracle/db-sample-schemas>`__ there are two
employees with the last name 'Smith' so the result is::
(159, 'Lindsey', 'Smith')
(171, 'William', 'Smith')
To return a REF CURSOR from a PL/SQL function, use ``cx_Oracle.DB_TYPE_CURSOR`` for the
return type of :meth:`Cursor.callfunc()`:
.. code-block:: python
ref_cursor = cursor.callfunc('example_package.f_get_cursor',
cx_Oracle.DB_TYPE_CURSOR)
for row in ref_cursor:
print(row)
See :ref:`tuning` for information on how to tune REF CURSORS.
Binding PL/SQL Collections
==========================
PL/SQL Collections like Associative Arrays can be bound as IN, OUT, and IN/OUT
variables. When binding IN values, an array can be passed directly as shown in
this example, which sums up the lengths of all of the strings in the provided
array. First the PL/SQL package definition:
.. code-block:: sql
create or replace package mypkg as
type udt_StringList is table of varchar2(100) index by binary_integer;
function DemoCollectionIn (
a_Values udt_StringList
) return number;
end;
/
create or replace package body mypkg as
function DemoCollectionIn (
a_Values udt_StringList
) return number is
t_ReturnValue number := 0;
begin
for i in 1..a_Values.count loop
t_ReturnValue := t_ReturnValue + length(a_Values(i));
end loop;
return t_ReturnValue;
end;
end;
/
Then the Python code:
.. code-block:: python
values = ["String One", "String Two", "String Three"]
return_val = cursor.callfunc("mypkg.DemoCollectionIn", int, [values])
print(return_val) # will print 32
In order get values back from the database, a bind variable must be created
using :meth:`Cursor.arrayvar()`. The first parameter to this method is a Python
type that cx_Oracle knows how to handle or one of the cx_Oracle :ref:`types`.
The second parameter is the maximum number of elements that the array can hold
or an array providing the value (and indirectly the maximum length). The final
parameter is optional and only used for strings and bytes. It identifies the
maximum length of the strings and bytes that can be stored in the array. If not
specified, the length defaults to 4000 bytes.
Consider the following PL/SQL package:
.. code-block:: sql
create or replace package mypkg as
type udt_StringList is table of varchar2(100) index by binary_integer;
procedure DemoCollectionOut (
a_NumElements number,
a_Values out nocopy udt_StringList
);
procedure DemoCollectionInOut (
a_Values in out nocopy udt_StringList
);
end;
/
create or replace package body mypkg as
procedure DemoCollectionOut (
a_NumElements number,
a_Values out nocopy udt_StringList
) is
begin
for i in 1..a_NumElements loop
a_Values(i) := 'Demo out element #' || to_char(i);
end loop;
end;
procedure DemoCollectionInOut (
a_Values in out nocopy udt_StringList
) is
begin
for i in 1..a_Values.count loop
a_Values(i) := 'Converted element #' || to_char(i) ||
' originally had length ' || length(a_Values(i));
end loop;
end;
end;
/
The Python code to process an OUT collection would look as follows. Note the
call to :meth:`Cursor.arrayvar()` which creates space for an array of strings.
Each string would permit up to 100 bytes and only 10 strings would be
permitted. If the PL/SQL block exceeds the maximum number of strings allowed
the error ``ORA-06513: PL/SQL: index for PL/SQL table out of range for host
language array`` would be raised.
.. code-block:: python
out_array_var = cursor.arrayvar(str, 10, 100)
cursor.callproc("mypkg.DemoCollectionOut", [5, out_array_var])
for val in out_array_var.getvalue():
print(val)
This would produce the following output::
Demo out element #1
Demo out element #2
Demo out element #3
Demo out element #4
Demo out element #5
The Python code to process an IN/OUT collections is similar. Note the different
call to :meth:`Cursor.arrayvar()` which creates space for an array of strings,
but uses an array to determine both the maximum length of the array and its
initial value.
.. code-block:: python
in_values = ["String One", "String Two", "String Three", "String Four"]
in_out_array_var = cursor.arrayvar(str, in_values)
cursor.callproc("mypkg.DemoCollectionInOut", [in_out_array_var])
for val in in_out_array_var.getvalue():
print(val)
This would produce the following output::
Converted element #1 originally had length 10
Converted element #2 originally had length 10
Converted element #3 originally had length 12
Converted element #4 originally had length 11
If an array variable needs to have an initial value but also needs to allow
for more elements than the initial value contains, the following code can be
used instead:
.. code-block:: python
in_out_array_var = cursor.arrayvar(str, 10, 100)
in_out_array_var.setvalue(0, ["String One", "String Two"])
All of the collections that have been bound in preceding examples have used
contiguous array elements. If an associative array with sparse array elements
is needed, a different approach is required. Consider the following PL/SQL
code:
.. code-block:: sql
create or replace package mypkg as
type udt_StringList is table of varchar2(100) index by binary_integer;
procedure DemoCollectionOut (
a_Value out nocopy udt_StringList
);
end;
/
create or replace package body mypkg as
procedure DemoCollectionOut (
a_Value out nocopy udt_StringList
) is
begin
a_Value(-1048576) := 'First element';
a_Value(-576) := 'Second element';
a_Value(284) := 'Third element';
a_Value(8388608) := 'Fourth element';
end;
end;
/
Note that the collection element indices are separated by large values. The
technique used above would fail with the exception ``ORA-06513: PL/SQL: index
for PL/SQL table out of range for host language array``. The code required to
process this collection looks like this instead:
.. code-block:: python
collection_type = connection.gettype("MYPKG.UDT_STRINGLIST")
collection = collection_type.newobject()
cursor.callproc("mypkg.DemoCollectionOut", [collection])
print(collection.aslist())
This produces the output::
['First element', 'Second element', 'Third element', 'Fourth element']
Note the use of :meth:`Object.aslist()` which returns the collection element
values in index order as a simple Python list. The indices themselves are lost
in this approach. Starting from cx_Oracle 7.0, the associative array can be
turned into a Python dictionary using :meth:`Object.asdict()`. If that value
was printed in the previous example instead, the output would be::
{-1048576: 'First element', -576: 'Second element', 284: 'Third element', 8388608: 'Fourth element'}
If the elements need to be traversed in index order, the methods
:meth:`Object.first()` and :meth:`Object.next()` can be used. The method
:meth:`Object.getelement()` can be used to acquire the element at a particular
index. This is shown in the following code:
.. code-block:: python
ix = collection.first()
while ix is not None:
print(ix, "->", collection.getelement(ix))
ix = collection.next(ix)
This produces the output::
-1048576 -> First element
-576 -> Second element
284 -> Third element
8388608 -> Fourth element
Similarly, the elements can be traversed in reverse index order using the
methods :meth:`Object.last()` and :meth:`Object.prev()` as shown in the
following code:
.. code-block:: python
ix = collection.last()
while ix is not None:
print(ix, "->", collection.getelement(ix))
ix = collection.prev(ix)
This produces the output::
8388608 -> Fourth element
284 -> Third element
-576 -> Second element
-1048576 -> First element
Binding PL/SQL Records
======================
PL/SQL record type objects can also be bound for IN, OUT and IN/OUT
bind variables. For example:
.. code-block:: sql
create or replace package mypkg as
type udt_DemoRecord is record (
NumberValue number,
StringValue varchar2(30),
DateValue date,
BooleanValue boolean
);
procedure DemoRecordsInOut (
a_Value in out nocopy udt_DemoRecord
);
end;
/
create or replace package body mypkg as
procedure DemoRecordsInOut (
a_Value in out nocopy udt_DemoRecord
) is
begin
a_Value.NumberValue := a_Value.NumberValue * 2;
a_Value.StringValue := a_Value.StringValue || ' (Modified)';
a_Value.DateValue := a_Value.DateValue + 5;
a_Value.BooleanValue := not a_Value.BooleanValue;
end;
end;
/
Then this Python code can be used to call the stored procedure which will
update the record:
.. code-block:: python
# create and populate a record
record_type = connection.gettype("MYPKG.UDT_DEMORECORD")
record = record_type.newobject()
record.NUMBERVALUE = 6
record.STRINGVALUE = "Test String"
record.DATEVALUE = datetime.datetime(2016, 5, 28)
record.BOOLEANVALUE = False
# show the original values
print("NUMBERVALUE ->", record.NUMBERVALUE)
print("STRINGVALUE ->", record.STRINGVALUE)
print("DATEVALUE ->", record.DATEVALUE)
print("BOOLEANVALUE ->", record.BOOLEANVALUE)
print()
# call the stored procedure which will modify the record
cursor.callproc("mypkg.DemoRecordsInOut", [record])
# show the modified values
print("NUMBERVALUE ->", record.NUMBERVALUE)
print("STRINGVALUE ->", record.STRINGVALUE)
print("DATEVALUE ->", record.DATEVALUE)
print("BOOLEANVALUE ->", record.BOOLEANVALUE)
This will produce the following output::
NUMBERVALUE -> 6
STRINGVALUE -> Test String
DATEVALUE -> 2016-05-28 00:00:00
BOOLEANVALUE -> False
NUMBERVALUE -> 12
STRINGVALUE -> Test String (Modified)
DATEVALUE -> 2016-06-02 00:00:00
BOOLEANVALUE -> True
Note that when manipulating records, all of the attributes must be set by the
Python program in order to avoid an Oracle Client bug which will result in
unexpected values or the Python application segfaulting.
.. _spatial:
Binding Spatial Datatypes
=========================
Oracle Spatial datatypes objects can be represented by Python objects and their
attribute values can be read and updated. The objects can further be bound and
committed to database. This is similar to the examples above.
An example of fetching SDO_GEOMETRY is in :ref:`Oracle Database Objects and
Collections <fetchobjects>`.
.. _inputtypehandlers:
Changing Bind Data Types using an Input Type Handler
====================================================
Input Type Handlers allow applications to change how data is bound to
statements, or even to enable new types to be bound directly.
An input type handler is enabled by setting the attribute
:attr:`Cursor.inputtypehandler` or :attr:`Connection.inputtypehandler`.
Input type handlers can be combined with variable converters to bind Python
objects seamlessly:
.. code-block:: python
# A standard Python object
class Building:
def __init__(self, build_id, description, num_floors, date_built):
self.building_id = build_id
self.description = description
self.num_floors = num_floors
self.date_built = date_built
building = Building(1, "Skyscraper 1", 5, datetime.date(2001, 5, 24))
# Get Python representation of the Oracle user defined type UDT_BUILDING
obj_type = con.gettype("UDT_BUILDING")
# convert a Python Building object to the Oracle user defined type
# UDT_BUILDING
def building_in_converter(value):
obj = obj_type.newobject()
obj.BUILDINGID = value.building_id
obj.DESCRIPTION = value.description
obj.NUMFLOORS = value.num_floors
obj.DATEBUILT = value.date_built
return obj
def input_type_handler(cursor, value, num_elements):
if isinstance(value, Building):
return cursor.var(obj_type, arraysize=num_elements,
inconverter=building_in_converter)
# With the input type handler, the bound Python object is converted
# to the required Oracle object before being inserted
cur.inputtypehandler = input_type_handler
cur.execute("insert into myTable values (:1, :2)", (1, building))
Binding Multiple Values to a SQL WHERE IN Clause
================================================
To use a SQL IN clause with multiple values, use one bind variable per
value. You cannot directly bind a Python list or dictionary to a single bind
variable. For example, to use two values in an IN clause:
.. code-block:: python
cursor.execute("""
select employee_id, first_name, last_name
from employees
where last_name in (:name1, :name2)""",
name1="Smith", name2="Taylor")
for row in cursor:
print(row)
This gives the output::
(159, 'Lindsey', 'Smith')
(171, 'William', 'Smith')
(176, 'Jonathon', 'Taylor')
(180, 'Winston', 'Taylor')
If the query is executed multiple times with differing numbers of values, a
bind variable should be included for each possible value. When the statement is
executed but the maximum number of values has not been supplied, the value
``None`` can be bound for missing values. For example, if the query above is
used for up to 5 values, the code would be:
.. code-block:: python
cursor.execute("""
select employee_id, first_name, last_name
from employees
where last_name in (:name1, :name2, :name3, :name4, :name5)""",
name1="Smith", name2="Taylor", name3=None, name4=None, name5=None)
for row in cursor:
print(row)
This will produce the same output as the original example. Reusing the same SQL
statement like this for a variable number of values, instead of constructing a
unique statement per set of values, allows best reuse of Oracle Database
resources.
However, if the statement is not going to be re-executed, or the number of
values is only going to be known at runtime, then a SQL statement can be built
up as follows:
.. code-block:: python
bind_values = ["Gates", "Marvin", "Fay"]
bind_names = [":" + str(i + 1) for i in range(len(bind_values))]
sql = "select employee_id, first_name, last_name from employees " + \
"where last_name in (%s)" % (",".join(bind_names))
cursor.execute(sql, bind_values)
for row in cursor:
print(row)
A general solution for a larger number of values is to construct a SQL
statement like::
SELECT ... WHERE col IN ( <something that returns a list of values> )
The best way to do the '<something that returns a list of values>' will depend
on how the data is initially represented and the number of items. You might
look at using CONNECT BY or at using a global temporary table.
One method is to use an Oracle collection with the ``TABLE()`` clause. For
example, if the following type was created::
SQL> CREATE OR REPLACE TYPE name_array AS TABLE OF VARCHAR2(25);
2 /
then the application could do:
.. code-block:: python
type_obj = connection.gettype("NAME_ARRAY")
obj = type_obj.newobject()
obj.extend(["Smith", "Taylor"])
cursor.execute("""select employee_id, first_name, last_name
from employees
where last_name in (select * from table(:1))""",
[obj])
for row in cursor:
print(row)
For efficiency, retain the return value of ``gettype()`` for reuse instead of
making repeated calls to get the type information.
Binding Column and Table Names
==============================
Column and table names cannot be bound in SQL queries. You can concatenate
text to build up a SQL statement, but make sure you use an Allow List or other
means to validate the data in order to avoid SQL Injection security issues:
.. code-block:: python
table_allow_list = ['employees', 'departments']
table_name = get_table_name() # get the table name from user input
if table_name.lower() not in table_allow_list:
raise Exception('Invalid table name')
sql = f'select * from {table_name}'
Binding column names can be done either by using the above method or by using a
CASE statement. The example below demonstrates binding a column name in an
ORDER BY clause:
.. code-block:: python
sql = """
SELECT * FROM departments
ORDER BY
CASE :bindvar
WHEN 'department_id' THEN DEPARTMENT_ID
ELSE MANAGER_ID
END"""
col_name = get_column_name() # Obtain a column name from the user
cursor.execute(sql, [col_name])
Depending on the name provided by the user, the query results will be
ordered either by the column ``DEPARTMENT_ID`` or the column ``MANAGER_ID``.

File diff suppressed because it is too large Load Diff

165
doc/src/user_guide/cqn.rst Normal file
View File

@ -0,0 +1,165 @@
.. _cqn:
***********************************
Continuous Query Notification (CQN)
***********************************
.. note::
**cx_Oracle has a major new release under a new name and homepage**
`python-oracledb <https://oracle.github.io/python-oracledb/>`__.
**New projects should install python-oracledb instead of cx_Oracle.**
`Continuous Query Notification (CQN)
<https://www.oracle.com/pls/topic/lookup?ctx=dblatest&
id=GUID-373BAF72-3E63-42FE-8BEA-8A2AEFBF1C35>`__ allows applications to receive
notifications when a table changes, such as when rows have been updated,
regardless of the user or the application that made the change. This can be
useful in many circumstances, such as near real-time monitoring, auditing
applications, or for such purposes as mid-tier cache invalidation. A cache
might hold some values that depend on data in a table. If the data in the
table changes, the cached values must then be updated with the new information.
CQN notification behavior is widely configurable. Choices include specifying
what types of SQL should trigger a notification, whether notifications should
survive database loss, and control over unsubscription. You can also choose
whether notification messages will include ROWIDs of affected rows.
By default, object-level notification (previously known as Database Change
Notification) occurs. With this mode a Python notification method is invoked
whenever a database transaction is committed that changes an object referenced
by a registered query. However if the :meth:`subscription
<Connection.subscribe>` option ``qos`` is :data:`cx_Oracle.SUBSCR_QOS_QUERY`
then query-level notification occurs. In this mode, the database notifies the
application whenever a committed transaction changes the result of a registered
query.
CQN is best used to track infrequent data changes.
Requirements
============
Before using CQN, users must have appropriate permissions:
.. code-block:: sql
GRANT CHANGE NOTIFICATION TO <user-name>;
To use CQN, connections must have ``events`` mode set to ``True``, for
example:
.. code-block:: python
connection = cx_Oracle.connect(user=user, password=password,
dsn="dbhost.example.com/orclpdb1",
events=True)
The default CQN connection mode means the database must be able to connect back
to the application using cx_Oracle in order to receive notification events.
Alternatively, when using Oracle Database and Oracle client libraries 19.4, or
later, subscriptions can set the optional ``client_initiated`` parameter to
``True``, see ``Connection.subscribe()`` below.
The default CQN connection mode typically means that the machine running
cx_Oracle needs a fixed IP address. Note :meth:`Connection.subscribe()` does
not verify that this reverse connection is possible. If there is any problem
sending a notification, then the callback method will not be invoked.
Configuration options can include an IP address and port on which cx_Oracle will
listen for notifications; otherwise, the database chooses values.
Creating a Subscription
=======================
Subscriptions allow Python to receives notifications for events that take place
in the database that match the given parameters.
For example, a basic CQN subscription might be created like:
.. code-block:: python
connection.subscribe(callback=my_callback)
See :meth:`Connection.subscribe()` for details on all of the parameters.
See :ref:`cqn-operation-codes` for the types of operations that are supported.
See :ref:`subscr-qos` for the quality of service values that are supported.
See :ref:`subscr-namespaces` and :ref:`subscr-protocols` for the namespaces and
protocols that are supported.
See :ref:`subscrobj` for more details on the subscription object that is
created.
When using Oracle Database and Oracle client libraries 19.4, or later, the
optional subscription parameter ``client_initiated`` can be set:
.. code-block:: python
connection.subscribe(callback=my_callback, client_initiated=True)
This enables CQN "client initiated" connections which internally use the same
approach as normal cx_Oracle connections to the database, and do not require the
database to be able to connect back to the application. Since client initiated
connections do not need special network configuration they have ease-of-use and
security advantages.
Registering Queries
===================
Once a subscription has been created, one or more queries must be registered by
calling :meth:`Subscription.registerquery()`. Registering a query behaves
similarly to :meth:`Cursor.execute()`, but only queries are permitted and the
``args`` parameter must be a sequence or dictionary.
An example script to receive query notifications when the 'REGIONS' table data
changes is:
.. code-block:: python
def cqn_callback(message):
print("Notification:")
for query in message.queries:
for tab in query.tables:
print("Table:", tab.name)
print("Operation:", tab.operation)
for row in tab.rows:
if row.operation & cx_Oracle.OPCODE_INSERT:
print("INSERT of rowid:", row.rowid)
if row.operation & cx_Oracle.OPCODE_DELETE:
print("DELETE of rowid:", row.rowid)
subscr = connection.subscribe(callback=cqn_callback,
operations=cx_Oracle.OPCODE_INSERT | cx_Oracle.OPCODE_DELETE,
qos=cx_Oracle.SUBSCR_QOS_QUERY | cx_Oracle.SUBSCR_QOS_ROWIDS)
subscr.registerquery("select * from regions")
input("Hit enter to stop CQN demo\n")
Running the above script, shows the initial output as::
Hit enter to stop CQN demo
Use SQL*Plus or another tool to commit a change to the table:
.. code-block:: sql
insert into regions values(120, 'L');
commit;
When the commit is executed, a notification will be received by the callback
which should print something like the following::
Hit enter to stop CQN demo
Notification:
Table: HR.REGIONS
Operation: 2
INSERT of rowid: AAA7EsAAHAAAFS/AAA
See `GitHub Samples
<https://github.com/oracle/python-cx_Oracle/blob/main/samples/cqn.py>`__
for a runnable CQN example.

View File

@ -0,0 +1,47 @@
.. _exception:
******************
Exception Handling
******************
.. note::
**cx_Oracle has a major new release under a new name and homepage**
`python-oracledb <https://oracle.github.io/python-oracledb/>`__.
**New projects should install python-oracledb instead of cx_Oracle.**
All exceptions raised by cx_Oracle are inherited from :attr:`cx_Oracle.Error`.
See :ref:`Exceptions <exceptions>` for more details on the various exceptions
defined by cx_Oracle. See the exception handling section in the
:ref:`API manual <exchandling>` for more details on the information available
when an exception is raised.
Applications can catch exceptions as needed. For example, when trying to add a
customer that already exists in the database, the following could be used
to catch the exception:
.. code-block:: python
try:
cursor.execute("insert into customer values (101, 'Customer A')")
except cx_Oracle.IntegrityError:
print("Customer ID already exists")
else:
print("Customer added")
If information about the exception needs to be processed instead, the following
code can be used:
.. code-block:: python
try:
cursor.execute("insert into customer values (101, 'Customer A')")
except cx_Oracle.IntegrityError as e:
error_obj, = e.args
print("Customer ID already exists")
print("Error Code:", error_obj.code)
print("Error Message:", error_obj.message)
else:
print("Customer added")

View File

@ -0,0 +1,164 @@
.. _globalization:
********************************
Character Sets and Globalization
********************************
.. note::
**cx_Oracle has a major new release under a new name and homepage**
`python-oracledb <https://oracle.github.io/python-oracledb/>`__.
**New projects should install python-oracledb instead of cx_Oracle.**
Data fetched from, and sent to, Oracle Database will be mapped between the
database character set and the "Oracle client" character set of the Oracle
Client libraries used by cx_Oracle. If data cannot be correctly mapped between
client and server character sets, then it may be corrupted or queries may fail
with :ref:`"codec can't decode byte" <codecerror>`.
cx_Oracle uses Oracles National Language Support (NLS) to assist in
globalizing applications. As well as character set support, there are many
other features that will be useful in applications. See the
`Database Globalization Support Guide
<https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=NLSPG>`__.
Setting the Client Character Set
================================
In cx_Oracle 8 the default encoding used for all character data changed to
"UTF-8". This universal encoding is suitable for most applications. If you
have a special need, you can pass the ``encoding`` and ``nencoding`` parameters
to the :meth:`cx_Oracle.connect` and :meth:`cx_Oracle.SessionPool` methods to
specify different Oracle Client character sets. For example:
.. code-block:: python
import cx_Oracle
connection = cx_Oracle.connect(dsn=connect_string, encoding="US-ASCII",
nencoding="UTF-8")
.. note::
In a future release of cx_Oracle, only UTF-8 will be supported.
The ``encoding`` parameter affects character data such as VARCHAR2 and CLOB
columns. The ``nencoding`` parameter affects "National Character" data such as
NVARCHAR2 and NCLOB. If you are not using national character types, then you
can omit ``nencoding``. Both the ``encoding`` and ``nencoding`` parameters are
expected to be one of the `Python standard encodings
<https://docs.python.org/3/library/codecs.html#standard-encodings>`__ such as
``UTF-8``. Do not accidentally use ``UTF8``, which Oracle uses to specify the
older Unicode 3.0 Universal character set, ``CESU-8``. Note that Oracle does
not recognize all of the encodings that Python recognizes. You can see which
encodings are usable in cx_Oracle by issuing this query:
.. code-block:: sql
select distinct utl_i18n.map_charset(value)
from v$nls_valid_values
where parameter = 'CHARACTERSET'
and utl_i18n.map_charset(value) is not null
order by 1
.. note::
From cx_Oracle 8, it is no longer possible to change the character set
using the ``NLS_LANG`` environment variable. The character set component
of that variable is ignored. The language and territory components of
``NLS_LANG`` are still respected by the Oracle Client libraries.
Character Set Example
---------------------
The script below tries to display data containing a Euro symbol from the
database.
.. code-block:: python
connection = cx_Oracle.connect(user=user, password=password,
dsn="dbhost.example.com/orclpdb1",
encoding="US-ASCII")
cursor = connection.cursor()
for row in cursor.execute("select nvarchar2_column from nchar_test"):
print(row)
Because the '€' symbol is not supported by the ``US-ASCII`` character set, all
'€' characters are replaced by '¿' in the cx_Oracle output::
('¿',)
When the ``encoding`` parameter is removed (or set to "UTF-8") during
connection:
.. code-block:: python
connection = cx_Oracle.connect(user=user, password=password,
dsn="dbhost.example.com/orclpdb1")
Then the output displays the Euro symbol as desired::
('€',)
.. _findingcharset:
Finding the Database and Client Character Set
---------------------------------------------
To find the database character set, execute the query:
.. code-block:: sql
SELECT value AS db_charset
FROM nls_database_parameters
WHERE parameter = 'NLS_CHARACTERSET';
To find the database 'national character set' used for NCHAR and related types,
execute the query:
.. code-block:: sql
SELECT value AS db_ncharset
FROM nls_database_parameters
WHERE parameter = 'NLS_NCHAR_CHARACTERSET';
To find the current "client" character set used by cx_Oracle, execute the
query:
.. code-block:: sql
SELECT DISTINCT client_charset AS client_charset
FROM v$session_connect_info
WHERE sid = SYS_CONTEXT('USERENV', 'SID');
If these character sets do not match, characters transferred over Oracle Net
will be mapped from one character set to another. This may impact performance
and may result in invalid data.
Setting the Oracle Client Locale
================================
You can use the ``NLS_LANG`` environment variable to set the language and
territory used by the Oracle Client libraries. For example, on Linux you could
set::
export NLS_LANG=JAPANESE_JAPAN
The language ("JAPANESE" in this example) specifies conventions such as the
language used for Oracle Database messages, sorting, day names, and month
names. The territory ("JAPAN") specifies conventions such as the default date,
monetary, and numeric formats. If the language is not specified, then the value
defaults to AMERICAN. If the territory is not specified, then the value is
derived from the language value. See `Choosing a Locale with the NLS_LANG
Environment Variable
<https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-86A29834-AE29-4BA5-8A78-E19C168B690A>`__
If the ``NLS_LANG`` environment variable is set in the application with
``os.environ['NLS_LANG']``, it must be set before any connection pool is
created, or before any standalone connections are created.
Other Oracle globalization variables, such as ``NLS_DATE_FORMAT`` can also be
set to change the behavior of cx_Oracle, see `Setting NLS Parameters
<https://www.oracle.com/pls/topic/lookup?ctx=dblatest&
id=GUID-6475CA50-6476-4559-AD87-35D431276B20>`__.

223
doc/src/user_guide/ha.rst Normal file
View File

@ -0,0 +1,223 @@
.. _highavailability:
********************************
High Availability with cx_Oracle
********************************
.. note::
**cx_Oracle has a major new release under a new name and homepage**
`python-oracledb <https://oracle.github.io/python-oracledb/>`__.
**New projects should install python-oracledb instead of cx_Oracle.**
Applications can utilize many features for high availability (HA) during planned and
unplanned outages in order to:
* Reduce application downtime
* Eliminate compromises between high availability and performance
* Increase operational productivity
.. _harecommend:
General HA Recommendations
--------------------------
General recommendations for creating highly available cx_Oracle programs are:
* Tune operating system and Oracle Network parameters to avoid long TCP timeouts, to prevent firewalls killing connections, and to avoid connection storms.
* Implement application error handling and recovery.
* Use the most recent version of the Oracle client libraries. New versions have improvements to features such as dead database server detection, and make it easier to set connection options.
* Use the most recent version of Oracle Database. New database versions introduce, and enhance, features such as Application Continuity (AC) and Transparent Application Continuity (TAC).
* Utilize Oracle Database technologies such as `RAC <https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=RACAD>`__ or standby databases.
* Configure database services to emit :ref:`FAN <fan>` events.
* Use a :ref:`connection pool <connpool>`, because pools can handle database events and take proactive and corrective action for draining, run time load balancing, and fail over. Set the minimum and maximum pool sizes to the same values to avoid connection storms. Remove resource manager or user profiles that prematurely close sessions.
* Test all scenarios thoroughly.
.. _hanetwork:
Network Configuration
---------------------
The operating system TCP and :ref:`Oracle Net configuration <optnetfiles>`
should be configured for performance and availability.
Options such as `SQLNET.OUTBOUND_CONNECT_TIMEOUT
<https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-0857C817-675F-4CF0-BFBB-C3667F119176>`__,
`SQLNET.RECV_TIMEOUT
<https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-4A19D81A-75F0-448E-B271-24E5187B5909>`__
and `SQLNET.SEND_TIMEOUT
<https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-48547756-9C0B-4D14-BE85-E7ADDD1A3A66>`__
can be explored.
`Oracle Net Services
<https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=NETRF>`__ options may
also be useful for high availability and performance tuning. For example the
database's `listener.ora` file can have `RATE_LIMIT
<https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-F302BF91-64F2-4CE8-A3C7-9FDB5BA6DCF8>`__
and `QUEUESIZE
<https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-FF87387C-1779-4CC3-932A-79BB01391C28>`__
parameters that can help handle connection storms.
With Oracle Client 19c, `EXPIRE_TIME
<https://docs.oracle.com/en/database/oracle/oracle-database/20/netrf/local-naming-parameters-in-tns-ora-file.html#GUID-6140611A-83FC-4C9C-B31F-A41FC2A5B12D>`__
can be used in :ref:`tnsnames.ora <optnetfiles>` connect descriptors to prevent
firewalls from terminating idle connections and to adjust keepalive timeouts.
The general recommendation for ``EXPIRE_TIME`` is to use a value that is
slightly less than half of the termination period. In older versions of Oracle
Client, a ``tnsnames.ora`` connect descriptor option `ENABLE=BROKEN
<https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-7A18022A-E40D-4880-B3CE-7EE9864756CA>`_
can be used instead of ``EXPIRE_TIME``. These settings can also aid detection
of a terminated remote database server.
When cx_Oracle uses :ref:`Oracle Client libraries 19c <archfig>`, then the
:ref:`Easy Connect Plus syntax <easyconnect>` syntax enables some options to be
used without needing a ``sqlnet.ora`` file. For example, if your firewall times
out every 4 minutes, and you cannot alter the firewall settings, then you may
decide to use ``EXPIRE_TIME`` in your connect string to send a probe every 2
minutes to the database to keep connections 'alive'::
connection = cx_Oracle.connect("hr", userpwd, "dbhost.example.com/orclpdb1?expire_time=2")
.. _fan:
Fast Application Notification (FAN)
-----------------------------------
Users of `Oracle Database FAN
<https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-F3FBE48B-468B-4393-8B0C-D5C8E0E4374D>`__
must connect to a FAN-enabled database service. The application should have
``events`` set to True when connecting. This value can also be changed via
:ref:`Oracle Client Configuration <optclientfiles>`.
FAN support is useful for planned and unplanned outages. It provides immediate
notification to cx_Oracle following outages related to the database, computers,
and networks. Without FAN, cx_Oracle can hang until a TCP timeout occurs and an
error is returned, which might be several minutes.
FAN allows cx_Oracle to provide high availability features without the
application being aware of an outage. Unused, idle connections in a
:ref:`connection pool <connpool>` will be automatically cleaned up. A future
:meth:`SessionPool.acquire()` call will establish a fresh connection to a
surviving database instance without the application being aware of any service
disruption.
To handle errors that affect active connections, you can add application logic
to re-connect (this will connect to a surviving database instance) and replay
application logic without having to return an error to the application user.
FAN benefits users of Oracle Database's clustering technology `Oracle RAC
<https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-D04AA2A7-2E68-4C5C-BD6E-36C62427B98E>`__
because connections to surviving database instances can be immediately made.
Users of Oracle's Data Guard with a broker will get FAN events generated when
the standby database goes online. Standalone databases will send FAN events
when the database restarts.
For a more information on FAN see the `white paper on Fast Application
Notification
<https://www.oracle.com/technetwork/database/options/clustering/applicationcontinuity/learnmore/fastapplicationnotification12c-2538999.pdf>`__.
.. _appcont:
Application Continuity (AC)
---------------------------
Oracle Application Continuity and Transparent Application Continuity are Oracle
Database technologies that record application interaction with the database and,
in the event of a database instance outage, attempt to replay the interaction on
a surviving database instance. If successful, users will be unaware of any
database issue. AC and TAC are best suited for OLTP applications.
When AC or TAC are configured on the database service, they are transparently
available to cx_Oracle applications.
You must thoroughly test your application because not all lower level calls in
the cx_Oracle implementation can be replayed.
See `OCI and Application Continuity
<https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-A8DD9422-2F82-42A9-9555-134296416E8F>`__
for more information.
.. _tg:
Transaction Guard
-----------------
cx_Oracle supports `Transaction Guard
<https://www.oracle.com/pls/topic/lookup?ctx=dblatest&
id=GUID-A675AF7B-6FF0-460D-A6E6-C15E7C328C8F>`__ which enables Python
application to verify the success or failure of the last transaction in the
event of an unplanned outage. This feature is available when both client and
database are 12.1 or higher.
Using Transaction Guard helps to:
* Preserve the commit outcome
* Ensure a known outcome for every transaction
See `Oracle Database Development Guide
<https://www.oracle.com/pls/topic/lookup?ctx=dblatest&
id=GUID-6C5880E5-C45F-4858-A069-A28BB25FD1DB>`__ for more information about
using Transaction Guard.
When an error occurs during commit, the Python application can acquire the
logical transaction id (``ltxid``) from the connection and then call a
procedure to determine the outcome of the commit for this logical transaction
id.
Follow the steps below to use the Transaction Guard feature in Python:
1. Grant execute privileges to the database users who will be checking the
outcome of the commit. Login as SYSDBA and run the following command:
.. code-block:: sql
GRANT EXECUTE ON DBMS_APP_CONT TO <username>;
2. Create a new service by executing the following PL/SQL block as SYSDBA.
Replace the ``<service-name>``, ``<network-name>`` and
``<retention-value>`` values with suitable values. It is important that the
``COMMIT_OUTCOME`` parameter be set to true for Transaction Guard to
function properly.
.. code-block:: sql
DECLARE
t_Params dbms_service.svc_parameter_array;
BEGIN
t_Params('COMMIT_OUTCOME') := 'true';
t_Params('RETENTION_TIMEOUT') := <retention-value>;
DBMS_SERVICE.CREATE_SERVICE('<service-name>', '<network-name>', t_Params);
END;
/
3. Start the service by executing the following PL/SQL block as SYSDBA:
.. code-block:: sql
BEGIN
DBMS_SERVICE.start_service('<service-name>');
END;
/
Ensure the service is running by examining the output of the following query:
.. code-block:: sql
SELECT name, network_name FROM V$ACTIVE_SERVICES ORDER BY 1;
**Python Application code requirements to use Transaction Guard**
In the Python application code:
* Use the connection attribute :attr:`~Connection.ltxid` to determine the
logical transaction id.
* Call the ``DBMS_APP_CONT.GET_LTXID_OUTCOME`` PL/SQL procedure with the
logical transaction id acquired from the connection attribute. This returns
a boolean value indicating if the last transaction was committed and whether
the last call was completed successfully or not.
See the `Transaction Guard Sample
<https://github.com/oracle/python-cx_Oracle/blob/main/
samples/transaction_guard.py>`__ for further details.

View File

@ -0,0 +1,415 @@
.. _initialization:
**************************
cx_Oracle 8 Initialization
**************************
.. note::
**cx_Oracle has a major new release under a new name and homepage**
`python-oracledb <https://oracle.github.io/python-oracledb/>`__.
**New projects should install python-oracledb instead of cx_Oracle.**
The cx_Oracle module loads Oracle Client libraries which communicate over
Oracle Net to an existing database. The Oracle Client libraries need to be
installed separately. See :ref:`installation`. Oracle Net is not a separate
product: it is how the Oracle Client and Oracle Database communicate.
.. figure:: /images/cx_Oracle_arch.png
cx_Oracle Architecture
.. _libinit:
Locating the Oracle Client Libraries
====================================
cx_Oracle dynamically loads the Oracle Client libraries using a search
heuristic. Only the first set of libraries found are loaded. The libraries
can be in an installation of Oracle Instant Client, in a full Oracle Client
installation, or in an Oracle Database installation (if Python is running on
the same machine as the database). The versions of Oracle Client and Oracle
Database do not have to be the same. For certified configurations see Oracle
Support's `Doc ID 207303.1
<https://support.oracle.com/epmos/faces/DocumentDisplay?id=207303.1>`__.
.. _wininit:
* On Windows, cx_Oracle looks for the Oracle Client libraries as follows:
- In the ``lib_dir`` directory specified in a call to
:meth:`cx_Oracle.init_oracle_client()`. This directory should contain
the libraries from an unzipped Instant Client 'Basic' or 'Basic Light'
package. If you pass the library directory from a full client or
database installation, such as Oracle Database "XE" Express Edition, then
you will need to have previously set your environment to use that
software installation, otherwise files such as message files will not be
located. On Windows when the path contains backslashes, use a 'raw'
string like ``lib_dir=r"C:\instantclient_19_6"``. If the Oracle Client
libraries cannot be loaded from ``lib_dir``, then an exception is raised.
- If ``lib_dir`` was not specified, then Oracle Client libraries are looked
for in the directory where the cx_Oracle binary module is installed.
This directory should contain the libraries from an unzipped Instant
Client 'Basic' or 'Basic Light' package. If the libraries are not found,
no exception is raised and the search continues, see next bullet point.
- In the directories on the system library search path, e.g. the ``PATH``
environment variable. If the Oracle Client libraries cannot be loaded,
then an exception is raised.
.. _macinit:
* On macOS, cx_Oracle looks for the Oracle Client libraries as follows:
- In the ``lib_dir`` directory specified in a call to
:meth:`cx_Oracle.init_oracle_client()`. This directory should contain
the libraries from an unzipped Instant Client 'Basic' or 'Basic Light'
package. If the Oracle Client libraries cannot be loaded from
``lib_dir``, then an exception is raised.
- If ``lib_dir`` was not specified, then Oracle Client libraries are looked
for in the directory where the cx_Oracle binary module is. This directory
should contain the libraries from an unzipped Instant Client 'Basic' or
'Basic Light' package. For example if
``/Users/your_username/Library/Python/3.8/lib/python/site-packages``
contains ``cx_Oracle.cpython-38-darwin.so``, then you could run ``ln -s
~/instantclient_19_3/libclntsh.dylib
~/Library/Python/3.8/lib/python/site-packages``. If the libraries are not
found, no exception is raised and the search continues, see next bullet
point.
- In the directories on the system library search path, e.g. ``~/lib/`` and
``/usr/local/lib``, or in ``$DYLD_LIBRARY_PATH``. These paths will vary
with macOS version and Python version. Any value in
``DYLD_LIBRARY_PATH`` will not propagate to a sub-shell. If the Oracle
Client libraries cannot be loaded, then an exception is raised.
.. _linuxinit:
* On Linux and related platforms, cx_Oracle looks for the Oracle Client
libraries as follows:
- In the ``lib_dir`` directory specified in a call to
:meth:`cx_Oracle.init_oracle_client()`.
**Note this is only useful to force immediate loading of the libraries
because on Linux and related platforms the libraries must always be in the
system library search path**.
The ``lib_dir`` directory should contain the libraries from an unzipped
Instant Client 'Basic' or 'Basic Light' package. If you pass the library
directory from a full client or database installation, such as Oracle
Database "XE" Express Edition then you will need to have previously set
the ``ORACLE_HOME`` environment variable. If the Oracle Client libraries
cannot be loaded from ``lib_dir``, then an exception is raised.
- If ``lib_dir`` was not specified, then Oracle Client libraries are looked
for in the operating system library search path, such as configured with
``ldconfig`` or set in the environment variable ``LD_LIBRARY_PATH``. On
some UNIX platforms an OS specific equivalent, such as ``LIBPATH`` or
``SHLIB_PATH`` is used instead of ``LD_LIBRARY_PATH``. If the libraries
are not found, no exception is raised and the search continues, see next
bullet point.
- In ``$ORACLE_HOME/lib``. Note the environment variable ``ORACLE_HOME``
should only ever be set when you have a full database installation or
full client installation. It should not be set if you are using Oracle
Instant Client. The ``ORACLE_HOME`` variable, and other necessary
variables, should be set before starting Python. See :ref:`envset`. If
the Oracle Client libraries cannot be loaded, then an exception is
raised.
If you call :meth:`cx_Oracle.init_oracle_client()` with a ``lib_dir``
parameter, the Oracle Client libraries are loaded immediately from that
directory. If you call :meth:`cx_Oracle.init_oracle_client()` but do *not* set
the ``lib_dir`` parameter, the Oracle Client libraries are loaded immediately
using the search heuristic above. If you do not call
:meth:`cx_Oracle.init_oracle_client()`, then the libraries are loaded using the
search heuristic when the first cx_Oracle function that depends on the
libraries is called, for example when a connection pool is created. If there
is a problem loading the libraries, then an exception is raised.
Make sure the Python process has directory and file access permissions for the
Oracle Client libraries. On Linux ensure a ``libclntsh.so`` file exists. On
macOS ensure a ``libclntsh.dylib`` file exists. cx_Oracle will not directly
load ``libclntsh.*.XX.1`` files in ``lib_dir`` or from the directory where the
cx_Oracle binary module is. Note other libraries used by ``libclntsh*`` are
also required.
To trace the loading of Oracle Client libraries, the environment variable
``DPI_DEBUG_LEVEL`` can be set to 64 before starting Python. For example, on
Linux, you might use::
$ export DPI_DEBUG_LEVEL=64
$ python myapp.py 2> log.txt
.. _usinginitoracleclient:
Using cx_Oracle.init_oracle_client() to set the Oracle Client directory
-----------------------------------------------------------------------
Applications can call the function :meth:`cx_Oracle.init_oracle_client()` to
specify the directory containing Oracle Instant Client libraries. The Oracle
Client Libraries are loaded when ``init_oracle_client()`` is called. For
example, if the Oracle Instant Client Libraries are in
``C:\oracle\instantclient_19_9`` on Windows or
``$HOME/Downloads/instantclient_19_8`` on macOS, then you can use:
.. code-block:: python
import cx_Oracle
import sys
import os
try:
if sys.platform.startswith("darwin"):
lib_dir = os.path.join(os.environ.get("HOME"), "Downloads",
"instantclient_19_8")
cx_Oracle.init_oracle_client(lib_dir=lib_dir)
elif sys.platform.startswith("win32"):
lib_dir=r"C:\oracle\instantclient_19_9"
cx_Oracle.init_oracle_client(lib_dir=lib_dir)
except Exception as err:
print("Whoops!")
print(err)
sys.exit(1)
Note the use of a 'raw' string ``r"..."`` on Windows so that backslashes are
treated as directory separators.
The :meth:`~cx_Oracle.init_oracle_client()` function can only be called once.
**Note if you set** ``lib_dir`` **on Linux and related platforms, you must still
have configured the system library search path to include that directory before
starting Python**.
On any operating system, if you set ``lib_dir`` to the library directory of a
full database or full client installation, you will need to have previously set
the Oracle environment, for example by setting the ``ORACLE_HOME`` environment
variable. Otherwise you will get errors like ORA-1804. You should set this,
and other Oracle environment variables, before starting Python, as
shown in :ref:`envset`.
.. _optnetfiles:
Optional Oracle Net Configuration Files
=======================================
Optional Oracle Net configuration files are read when cx_Oracle is loaded.
These files affect connections and applications. The common files are:
* ``tnsnames.ora``: A configuration file that defines databases addresses
for establishing connections. See :ref:`Net Service Name for Connection
Strings <netservice>`.
* ``sqlnet.ora``: A profile configuration file that may contain information
on features such as connection failover, network encryption, logging, and
tracing. See `Oracle Net Services Reference
<https://www.oracle.com/pls/topic/lookup?ctx=dblatest&
id=GUID-19423B71-3F6C-430F-84CC-18145CC2A818>`__ for more information.
The files should be in a directory accessible to Python, not on the database
server host.
For example, if the file ``/etc/my-oracle-config/tnsnames.ora`` should be used,
you can call :meth:`cx_Oracle.init_oracle_client()`:
.. code-block:: python
import cx_Oracle
import sys
try:
cx_Oracle.init_oracle_client(config_dir="/etc/my-oracle-config")
except Exception as err:
print("Whoops!")
print(err)
sys.exit(1)
This is equivalent to setting the environment variable `TNS_ADMIN
<https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-12C94B15-2CE1-4B98-9D0C-8226A9DDF4CB>`__
to ``/etc/my-oracle-config``.
If :meth:`~cx_Oracle.init_oracle_client()` is not called, or it is called but
``config_dir`` is not specified, then default directories searched for the
configuration files. They include:
* ``$TNS_ADMIN``
* ``/opt/oracle/instantclient_19_6/network/admin`` if Instant Client is in ``/opt/oracle/instantclient_19_6``.
* ``/usr/lib/oracle/19.6/client64/lib/network/admin`` if Oracle 19.6 Instant Client RPMs are used on Linux.
* ``$ORACLE_HOME/network/admin`` if cx_Oracle is using libraries from a database installation.
A wallet configuration file ``cwallet.sso`` for secure connection can be
located with, or separately from, the ``tnsnames.ora`` and ``sqlnet.ora``
files. It should be securely stored. The ``sqlnet.ora`` file's
``WALLET_LOCATION`` path should be set to the directory containing
``cwallet.sso``. For Oracle Autonomous Database use of wallets, see
:ref:`autonomousdb`.
Note the :ref:`easyconnect` can set many common configuration options without
needing ``tnsnames.ora`` or ``sqlnet.ora`` files.
The section :ref:`Network Configuration <hanetwork>` has some discussion about
Oracle Net configuration.
.. _optclientfiles:
Optional Oracle Client Configuration Files
==========================================
When cx_Oracle uses Oracle Client libraries version 12.1, or later, an optional
client parameter file called ``oraaccess.xml`` can be used to configure some
behviors of those libraries, such as statement caching and prefetching. This can
be useful if the application cannot be altered. The file is read from the same
directory as the `Optional Oracle Net Configuration Files`_.
A sample ``oraaccess.xml`` file that sets the Oracle client prefetch value to
1000 rows. This value affects every SQL query in the application::
<?xml version="1.0"?>
<oraaccess xmlns="http://xmlns.oracle.com/oci/oraaccess"
xmlns:oci="http://xmlns.oracle.com/oci/oraaccess"
schemaLocation="http://xmlns.oracle.com/oci/oraaccess
http://xmlns.oracle.com/oci/oraaccess.xsd">
<default_parameters>
<prefetch>
<rows>1000</rows>
</prefetch>
</default_parameters>
</oraaccess>
Prefetching is the number of additional rows the underlying Oracle client
library fetches whenever cx_Oracle requests query data from the database.
Prefetching is a tuning option to maximize data transfer efficiency and minimize
:ref:`round-trips <roundtrips>` to the database. The prefetch size does not
affect when, or how many, rows are returned by cx_Oracle to the application.
The cache management is transparently handled by the Oracle client libraries.
Note, standard cx_Oracle fetch tuning is via :attr:`Cursor.arraysize`, but
changing the prefetch value can be useful in some cases such as when modifying
the application is not feasible.
The `oraaccess.xml` file has other uses including:
- Changing the value of Fast Application Notification :ref:`FAN <fan>` events which affects notifications and Runtime Load Balancing (RLB).
- Configuring `Client Result Caching <https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-D2FA7B29-301B-4AB8-8294-2B1B015899F9>`__ parameters
- Turning on `Client Statement Cache Auto-tuning <https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-6E21AA56-5BBE-422A-802C-197CAC8AAEA4>`__
Refer to the documentation on `oraaccess.xml
<https://www.oracle.com/pls/topic/lookup?
ctx=dblatest&id=GUID-9D12F489-EC02-46BE-8CD4-5AECED0E2BA2>`__
for more details.
.. _envset:
Oracle Environment Variables
============================
Some common environment variables that influence cx_Oracle are shown below. The
variables that may be needed depend on how Python is installed, how you connect
to the database, and what optional settings are desired. It is recommended to
set Oracle variables in the environment before invoking Python, however they may
also be set in the application with ``os.putenv()`` before the first connection
is established. System environment variables like ``LD_LIBRARY_PATH`` must be
set before Python starts.
.. list-table:: Common Oracle environment variables
:header-rows: 1
:widths: 1 2
:align: left
* - Oracle Environment Variables
- Purpose
* - LD_LIBRARY_PATH
- The library search path for platforms like Linux should include the
Oracle libraries, for example ``$ORACLE_HOME/lib`` or
``/opt/instantclient_19_3``. This variable is not needed if the
libraries are located by an alternative method, such as with
``ldconfig``. On other UNIX platforms you may need to set an OS
specific equivalent, such as ``LIBPATH`` or ``SHLIB_PATH``.
* - PATH
- The library search path for Windows should include the location where
``OCI.DLL`` is found. Not needed if you set ``lib_dir`` in a call to
:meth:`cx_Oracle.init_oracle_client()`
* - TNS_ADMIN
- The directory of optional Oracle Client configuration files such as
``tnsnames.ora`` and ``sqlnet.ora``. Not needed if the configuration
files are in a default location or if ``config_dir`` was not used in
:meth:`cx_Oracle.init_oracle_client()`. See :ref:`optnetfiles`.
* - ORA_SDTZ
- The default session time zone.
* - ORA_TZFILE
- The name of the Oracle time zone file to use. See below.
* - ORACLE_HOME
- The directory containing the Oracle Database software. The directory
and various configuration files must be readable by the Python process.
This variable should not be set if you are using Oracle Instant Client.
* - NLS_LANG
- Determines the 'national language support' globalization options for
cx_Oracle. Note: from cx_Oracle 8, the character set component is
ignored and only the language and territory components of ``NLS_LANG``
are used. The character set can instead be specified during connection
or connection pool creation. See :ref:`globalization`.
* - NLS_DATE_FORMAT, NLS_TIMESTAMP_FORMAT
- Often set in Python applications to force a consistent date format
independent of the locale. The variables are ignored if the environment
variable ``NLS_LANG`` is not set.
Oracle Instant Client includes a small and big time zone file, for example
``timezone_32.dat`` and ``timezlrg_32.dat``. The versions can be shown by running
the utility ``genezi -v`` located in the Instant Client directory. The small file
contains only the most commonly used time zones. By default the larger
``timezlrg_n.dat`` file is used. If you want to use the smaller ``timezone_n.dat``
file, then set the ``ORA_TZFILE`` environment variable to the name of the file
without any directory prefix, for example ``export ORA_TZFILE=timezone_32.dat``.
With Oracle Instant Client 12.2 or later, you can also use an external time zone
file. Create a subdirectory ``oracore/zoneinfo`` under the Instant Client
directory, and move the file into it. Then set ``ORA_TZFILE`` to the file name,
without any directory prefix. The ``genezi -v`` utility will show the time zone
file in use.
If cx_Oracle is using Oracle Client libraries from an Oracle Database or full
Oracle Client software installation, and you want to use a non-default time zone
file, then set ``ORA_TZFILE`` to the file name with a directory prefix, for
example: ``export ORA_TZFILE=/opt/oracle/myconfig/timezone_31.dat``.
The Oracle Database documentation contains more information about time zone
files, see `Choosing a Time Zone File
<https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-805AB986-DE12-4FEA-AF56-5AABCD2132DF>`__.
.. _otherinit:
Other cx_Oracle Initialization
==============================
The :meth:`cx_Oracle.init_oracle_client()` function allows ``driver_name`` and
``error_url`` parameters to be set. These are useful for applications whose
end-users are not aware cx_Oracle is being used. An example of setting the
parameters is:
.. code-block:: python
import cx_Oracle
import sys
try:
cx_Oracle.init_oracle_client(driver_name="My Great App : 3.1.4",
error_url="https://example.com/MyInstallInstructions.html")
except Exception as err:
print("Whoops!")
print(err)
sys.exit(1)
The convention for ``driver_name`` is to separate the product name from the
product version by a colon and single blank characters. The value will be shown
in Oracle Database views like ``V$SESSION_CONNECT_INFO``. If this parameter is
not specified, then the value "cx_Oracle : *version*" is used.
The ``error_url`` string will be shown in the exception raised if
``init_oracle_client()`` cannot load the Oracle Client libraries. This allows
applications that use cx_Oracle to refer users to application-specific
installation instructions. If this value is not specified, then the
:ref:`installation` URL is used.

View File

@ -0,0 +1,936 @@
.. _installation:
************************
cx_Oracle 8 Installation
************************
.. note::
**cx_Oracle has a major new release under a new name and homepage**
`python-oracledb <https://oracle.github.io/python-oracledb/>`__.
**New projects should install python-oracledb instead of cx_Oracle.**
Overview
========
To use cx_Oracle 8.3 with Python and Oracle Database you need:
- Python 3.6 and higher. Older versions of cx_Oracle may work with older
versions of Python.
- Oracle Client libraries. These can be from the free `Oracle Instant Client
<https://www.oracle.com/database/technologies/instant-client.html>`__, from a
full Oracle Client installation, or from those included in Oracle Database if
Python is on the same machine as the database. Oracle client libraries
versions 21, 19, 18, 12, and 11.2 are supported where available on Linux,
Windows and macOS (Intel x86). Users have also reported success with other
platforms. Use the latest client possible: Oracle's standard client-server
version interoperability allows connection to both older and newer databases.
- An Oracle Database, either local or remote.
The cx_Oracle module loads Oracle Client libraries which communicate
over Oracle Net to an existing database. Oracle Net is not a separate
product: it is how the Oracle Client and Oracle Database communicate.
.. figure:: /images/cx_Oracle_arch.png
cx_Oracle Architecture
Quick Start cx_Oracle Installation
==================================
You can:
- Install `Python <https://www.python.org/downloads>`__ 3, if not already
available. On macOS you must always install your own Python.
Python 3.6 and higher are supported by cx_Oracle 8.3. If you use Python 2,
then the older cx_Oracle 7.3 will install.
- Install cx_Oracle from `PyPI
<https://pypi.org/project/cx-Oracle/>`__ with:
.. code-block:: shell
python -m pip install cx_Oracle --upgrade
Note: if a binary wheel package is not available for your platform,
the source package will be downloaded instead. This will be compiled
and the resulting binary installed.
The ``--user`` option may be useful, if you don't have permission to write to
system directories:
.. code-block:: shell
python -m pip install cx_Oracle --upgrade --user
If you are behind a proxy, add a proxy server to the command, for example add
``--proxy=http://proxy.example.com:80``
- Add Oracle 21, 19, 18, 12 or 11.2 client libraries to your operating system
library search path such as ``PATH`` on Windows or ``LD_LIBRARY_PATH`` on
Linux. On macOS use :meth:`~cx_Oracle.init_oracle_client()` in your
application to pass the Oracle Client directory name, see
:ref:`usinginitoracleclient`. This is also usable on Windows.
To get the libraries:
- If your database is on a remote computer, then download and unzip the client
libraries from the free `Oracle Instant Client
<https://www.oracle.com/database/technologies/instant-client.html>`__
"Basic" or "Basic Light" package for your operating system
architecture.
Instant Client on Windows requires an appropriate Microsoft Windows
Redistributables, see :ref:`wininstall`. On Linux, the ``libaio``
(sometimes called ``libaio1``) package is needed. Oracle Linux 8 also
needs the ``libnsl`` package.
- Alternatively, use the client libraries already available in a
locally installed database such as the free `Oracle Database
Express Edition ("XE")
<https://www.oracle.com/database/technologies/appdev/xe.html>`__
release.
Version 21 client libraries can connect to Oracle Database 12.1 or greater.
Version 19, 18 and 12.2 client libraries can connect to Oracle Database 11.2
or greater. Version 12.1 client libraries can connect to Oracle Database 10.2
or greater. Version 11.2 client libraries can connect to Oracle Database 9.2
or greater.
- Create a script like the one below:
.. code-block:: python
# myscript.py
import cx_Oracle
# Connect as user "hr" with password "welcome" to the "orclpdb1" service running on this computer.
connection = cx_Oracle.connect(user="hr", password="welcome",
dsn="localhost/orclpdb1")
cursor = connection.cursor()
cursor.execute("""
SELECT first_name, last_name
FROM employees
WHERE department_id = :did AND employee_id > :eid""",
did = 50,
eid = 190)
for fname, lname in cursor:
print("Values:", fname, lname)
Locate your Oracle Database username and password, and the database
connection string. The connection string is commonly of the format
``hostname/servicename``, using the hostname where the database is
running, and using the service name of the Oracle Database instance.
Substitute your username, password and connection string in the
code. Run the Python script, for example::
python myscript.py
You can learn how to use cx_Oracle from the :ref:`API documentation <module>`
and `samples
<https://github.com/oracle/python-cx_Oracle/blob/main/samples>`__.
If you run into installation trouble, check out the section on `Troubleshooting`_.
Oracle Client and Oracle Database Interoperability
==================================================
cx_Oracle requires Oracle Client libraries. The libraries provide the
necessary network connectivity to access an Oracle Database instance.
They also provide basic and advanced connection management and data
features to cx_Oracle.
The simplest way to get Oracle Client libraries is to install the free
`Oracle Instant Client
<https://www.oracle.com/database/technologies/instant-client.html>`__
"Basic" or "Basic Light" package. The libraries are also available in
any Oracle Database installation or full Oracle Client installation.
Oracle's standard client-server network interoperability allows
connections between different versions of Oracle Client libraries and
Oracle Database. For certified configurations see Oracle Support's
`Doc ID 207303.1
<https://support.oracle.com/epmos/faces/DocumentDisplay?id=207303.1>`__.
In summary, Oracle Client 21 can connect to Oracle Database 12.1 or greater.
Oracle Client 19, 18 and 12.2 can connect to Oracle Database 11.2 or
greater. Oracle Client 12.1 can connect to Oracle Database 10.2 or
greater. Oracle Client 11.2 can connect to Oracle Database 9.2 or greater. The
technical restrictions on creating connections may be more flexible. For
example Oracle Client 12.2 can successfully connect to Oracle Database 10.2.
cx_Oracle uses the shared library loading mechanism available on each
supported platform to load the Oracle Client libraries at runtime. It
does not need to be rebuilt for different versions of the libraries.
Since a single cx_Oracle binary can use different client versions and
also access multiple database versions, it is important your
application is tested in your intended release environments. Newer
Oracle clients support new features, such as the `oraaccess.xml
<https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-9D12F489-EC02-46BE-8CD4-5AECED0E2BA2>`__ external configuration
file available with 12.1 or later clients, session pool improvements,
improved high availability features, call timeouts, and `other enhancements
<https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-D60519C3-406F-4588-8DA1-D475D5A3E1F6>`__.
The cx_Oracle function :func:`~cx_Oracle.clientversion()` can be used to
determine which Oracle Client version is in use. The attribute
:attr:`Connection.version` can be used to determine which Oracle Database
version a connection is accessing. These can then be used to adjust application
behavior accordingly. Attempts to use Oracle features that are not supported by
a particular client/server library combination will result in runtime errors.
Installing cx_Oracle on Linux
=============================
This section discusses the generic installation methods on Linux. To use Python
and cx_Oracle RPM packages from yum on Oracle Linux, see :ref:`oraclelinux`.
.. note::
**cx_Oracle has a major new release under a new name and homepage**
`python-oracledb <https://oracle.github.io/python-oracledb/>`__.
**New projects should install python-oracledb instead of cx_Oracle.**
Install cx_Oracle
-----------------
The generic way to install cx_Oracle on Linux is to use Python's `Pip
<https://pip.readthedocs.io/en/latest/installing/>`__ package to
install cx_Oracle from `PyPI
<https://pypi.org/project/cx-Oracle/>`__:
.. code-block:: shell
python -m pip install cx_Oracle --upgrade
The ``--user`` option may be useful, if you don't have permission to write to
system directories:
.. code-block:: shell
python -m pip install cx_Oracle --upgrade --user
If you are behind a proxy, add a proxy server to the command, for example add
``--proxy=http://proxy.example.com:80``
This will download and install a pre-compiled binary `if one is
available <https://pypi.org/project/cx-Oracle/>`__ for your
architecture. If a pre-compiled binary is not available, the source
will be downloaded, compiled, and the resulting binary installed.
Compiling cx_Oracle requires the ``Python.h`` header file. If you are
using the default ``python`` package, this file is in the ``python-devel``
package or equivalent.
Install Oracle Client
---------------------
Using cx_Oracle requires Oracle Client libraries to be installed.
These provide the necessary network connectivity allowing cx_Oracle
to access an Oracle Database instance.
- If your database is on a remote computer, then download the free `Oracle
Instant Client
<https://www.oracle.com/database/technologies/instant-client.html>`__
"Basic" or "Basic Light" package for your operating system
architecture. Use the RPM or ZIP packages, based on your
preferences.
- Alternatively, use the client libraries already available in a
locally installed database such as the free `Oracle Database
Express Edition ("XE")
<https://www.oracle.com/database/technologies/appdev/xe.html>`__
release.
Oracle Instant Client Zip Files
+++++++++++++++++++++++++++++++
To use cx_Oracle with Oracle Instant Client zip files:
1. Download an Oracle 21, 19, 18, 12, or 11.2 "Basic" or "Basic Light" zip file
matching your Python 64-bit or 32-bit architecture:
- `x86-64 64-bit <https://www.oracle.com/database/technologies/instant-client/linux-x86-64-downloads.html>`__
- `x86 32-bit <https://www.oracle.com/database/technologies/instant-client/linux-x86-32-downloads.html>`__
- `ARM (aarch64) 64-bit <https://www.oracle.com/database/technologies/instant-client/linux-arm-aarch64-downloads.html>`__
The latest version is recommended. Oracle Instant Client 21 will connect to
Oracle Database 12.1 or later.
2. Unzip the package into a single directory that is accessible to your
application. For example:
.. code-block:: shell
mkdir -p /opt/oracle
cd /opt/oracle
unzip instantclient-basic-linux.x64-21.1.0.0.0.zip
3. Install the ``libaio`` package with sudo or as the root user. For example::
sudo yum install libaio
On some Linux distributions this package is called ``libaio1`` instead.
On recent Linux versions such as Oracle Linux 8, you may also need to
install the ``libnsl`` package when using Oracle Instant Client 19.
4. If there is no other Oracle software on the machine that will be
impacted, permanently add Instant Client to the runtime link
path. For example, with sudo or as the root user:
.. code-block:: shell
sudo sh -c "echo /opt/oracle/instantclient_21_1 > /etc/ld.so.conf.d/oracle-instantclient.conf"
sudo ldconfig
Alternatively, set the environment variable ``LD_LIBRARY_PATH`` to
the appropriate directory for the Instant Client version. For
example::
export LD_LIBRARY_PATH=/opt/oracle/instantclient_21_1:$LD_LIBRARY_PATH
5. If you use optional Oracle configuration files such as ``tnsnames.ora``,
``sqlnet.ora`` or ``oraaccess.xml`` with Instant Client, then put the files
in an accessible directory, for example in
``/opt/oracle/your_config_dir``. Then use:
.. code-block:: python
import cx_Oracle
cx_Oracle.init_oracle_client(config_dir="/home/your_username/oracle/your_config_dir")
Or set the environment variable ``TNS_ADMIN`` to that directory name.
Alternatively, put the files in the ``network/admin`` subdirectory of Instant
Client, for example in ``/opt/oracle/instantclient_21_1/network/admin``.
This is the default Oracle configuration directory for executables linked
with this Instant Client.
Oracle Instant Client RPMs
++++++++++++++++++++++++++
To use cx_Oracle with Oracle Instant Client RPMs:
1. Download an Oracle 21,19, 18, 12, or 11.2 "Basic" or "Basic Light" RPM
matching your Python architecture:
- `x86-64 64-bit <https://www.oracle.com/database/technologies/instant-client/linux-x86-64-downloads.html>`__
- `x86 32-bit <https://www.oracle.com/database/technologies/instant-client/linux-x86-32-downloads.html>`__
- `ARM (aarch64) 64-bit <https://www.oracle.com/database/technologies/instant-client/linux-arm-aarch64-downloads.html>`__
Oracle's yum server has convenient repositories:
- `Instant Client 21 RPMs for Oracle Linux x86-64 8 <https://yum.oracle.com/repo/OracleLinux/OL8/oracle/instantclient21/x86_64/index.html>`__, `Older Instant Client RPMs for Oracle Linux x86-64 8 <https://yum.oracle.com/repo/OracleLinux/OL8/oracle/instantclient/x86_64/index.html>`__
- `Instant Client 21 RPMs for Oracle Linux x86-64 7 <https://yum.oracle.com/repo/OracleLinux/OL7/oracle/instantclient21/x86_64/index.html>`__, `Older Instant Client RPMs for Oracle Linux x86-64 7 <https://yum.oracle.com/repo/OracleLinux/OL7/oracle/instantclient/x86_64/index.html>`__
- `Instant Client RPMs for Oracle Linux x86-64 6 <https://yum.oracle.com/repo/OracleLinux/OL6/oracle/instantclient/x86_64/index.html>`__
- `Instant Client RPMs for Oracle Linux ARM (aarch64) 8 <https://yum.oracle.com/repo/OracleLinux/OL8/oracle/instantclient/aarch64/index.html>`__
- `Instant Client RPMs for Oracle Linux ARM (aarch64) 7 <https://yum.oracle.com/repo/OracleLinux/OL7/oracle/instantclient/aarch64/index.html>`__
The latest version is recommended. Oracle Instant Client 21 will connect to
Oracle Database 12.1 or later.
2. Install the downloaded RPM with sudo or as the root user. For example:
.. code-block:: shell
sudo yum install oracle-instantclient-basic-21.1.0.0.0-1.x86_64.rpm
Yum will automatically install required dependencies, such as ``libaio``.
On recent Linux versions, such as Oracle Linux 8, you may need to manually
install the ``libnsl`` package when using Oracle Instant Client 19.
3. For Instant Client 19, or later, the system library search path is
automatically configured during installation.
For older versions, if there is no other Oracle software on the machine that will be
impacted, permanently add Instant Client to the runtime link
path. For example, with sudo or as the root user:
.. code-block:: shell
sudo sh -c "echo /usr/lib/oracle/18.5/client64/lib > /etc/ld.so.conf.d/oracle-instantclient.conf"
sudo ldconfig
Alternatively, for version 18 and earlier, every shell running
Python will need to have the environment variable
``LD_LIBRARY_PATH`` set to the appropriate directory for the
Instant Client version. For example::
export LD_LIBRARY_PATH=/usr/lib/oracle/18.5/client64/lib:$LD_LIBRARY_PATH
4. If you use optional Oracle configuration files such as ``tnsnames.ora``,
``sqlnet.ora`` or ``oraaccess.xml`` with Instant Client, then put the files
in an accessible directory, for example in
``/opt/oracle/your_config_dir``. Then use:
.. code-block:: python
import cx_Oracle
cx_Oracle.init_oracle_client(config_dir="/opt/oracle/your_config_dir")
Or set the environment variable ``TNS_ADMIN`` to that directory name.
Alternatively, put the files in the ``network/admin`` subdirectory of Instant
Client, for example in ``/usr/lib/oracle/21/client64/lib/network/admin``.
This is the default Oracle configuration directory for executables linked
with this Instant Client.
Local Database or Full Oracle Client
++++++++++++++++++++++++++++++++++++
cx_Oracle applications can use Oracle Client 21, 19, 18, 12, or 11.2 libraries
from a local Oracle Database or full Oracle Client installation.
The libraries must be either 32-bit or 64-bit, matching your
Python architecture.
1. Set required Oracle environment variables by running the Oracle environment
script. For example:
.. code-block:: shell
source /usr/local/bin/oraenv
For Oracle Database Express Edition ("XE") 11.2, run:
.. code-block:: shell
source /u01/app/oracle/product/11.2.0/xe/bin/oracle_env.sh
2. Optional Oracle configuration files such as ``tnsnames.ora``,
``sqlnet.ora`` or ``oraaccess.xml`` can be placed in
``$ORACLE_HOME/network/admin``.
Alternatively, Oracle configuration files can be put in another,
accessible directory. Then set the environment variable
``TNS_ADMIN`` to that directory name.
.. _oraclelinux:
Installing cx_Oracle RPMs on Oracle Linux
=========================================
Python and cx_Oracle RPM packages are available from the `Oracle Linux yum server
<https://yum.oracle.com/>`__. Various versions of Python are easily installed.
Using the yum server makes it easy to keep up to date.
Installation instructions are at `Oracle Linux for Python
Developers <https://yum.oracle.com/oracle-linux-python.html>`__.
.. _wininstall:
Installing cx_Oracle on Windows
===============================
.. note::
**cx_Oracle has a major new release under a new name and homepage**
`python-oracledb <https://oracle.github.io/python-oracledb/>`__.
**New projects should install python-oracledb instead of cx_Oracle.**
Install cx_Oracle
-----------------
Use Python's `Pip <https://pip.readthedocs.io/en/latest/installing/>`__
package to install cx_Oracle from `PyPI
<https://pypi.org/project/cx-Oracle/>`__::
python -m pip install cx_Oracle --upgrade
If you are behind a proxy, specify your proxy server:
.. code-block:: shell
python -m pip install cx_Oracle --proxy=http://proxy.example.com:80 --upgrade
This will download and install a pre-compiled binary `if one is
available <https://pypi.org/project/cx-Oracle/>`__ for your
architecture. If a pre-compiled binary is not available, the source
will be downloaded, compiled, and the resulting binary installed.
Install Oracle Client
---------------------
Using cx_Oracle requires Oracle Client libraries to be installed.
These provide the necessary network connectivity allowing cx_Oracle
to access an Oracle Database instance. Oracle Client versions 19, 18,
12 and 11.2 are supported.
- If your database is on a remote computer, then download the free `Oracle
Instant Client
<https://www.oracle.com/database/technologies/instant-client.html>`__
"Basic" or "Basic Light" package for your operating system
architecture.
- Alternatively, use the client libraries already available in a
locally installed database such as the free `Oracle Database
Express Edition ("XE")
<https://www.oracle.com/database/technologies/appdev/xe.html>`__
release.
Oracle Instant Client Zip Files
+++++++++++++++++++++++++++++++
To use cx_Oracle with Oracle Instant Client zip files:
1. Download an Oracle 19, 18, 12, or 11.2 "Basic" or "Basic Light" zip
file: `64-bit
<https://www.oracle.com/database/technologies/instant-client/winx64-64-downloads.html>`__
or `32-bit
<https://www.oracle.com/database/technologies/instant-client/microsoft-windows-32-downloads.html>`__, matching your
Python architecture.
The latest version is recommended. Oracle Instant Client 19 will
connect to Oracle Database 11.2 or later.
Windows 7 users: Note that Oracle 19c is not supported on Windows 7.
2. Unzip the package into a directory that is accessible to your
application. For example unzip
``instantclient-basic-windows.x64-19.11.0.0.0dbru.zip`` to
``C:\oracle\instantclient_19_11``.
3. Oracle Instant Client libraries require a Visual Studio redistributable with
a 64-bit or 32-bit architecture to match Instant Client's architecture.
Each Instant Client version requires a different redistributable version:
- For Instant Client 21 install `VS 2019 <https://docs.microsoft.com/en-US/cpp/windows/latest-supported-vc-redist?view=msvc-170>`__ or later.
- For Instant Client 19 install `VS 2017 <https://docs.microsoft.com/en-US/cpp/windows/latest-supported-vc-redist?view=msvc-170>`__.
- For Instant Client 18 or 12.2 install `VS 2013 <https://docs.microsoft.com/en-US/cpp/windows/latest-supported-vc-redist?view=msvc-170#visual-studio-2013-vc-120>`__
- For Instant Client 12.1 install `VS 2010 <https://docs.microsoft.com/en-US/cpp/windows/latest-supported-vc-redist?view=msvc-170#visual-studio-2010-vc-100-sp1-no-longer-supported>`__
- For Instant Client 11.2 install `VS 2005 64-bit <https://docs.microsoft.com/en-US/cpp/windows/latest-supported-vc-redist?view=msvc-170#visual-studio-2005-vc-80-sp1-no-longer-supported>`__
Configure Oracle Instant Client
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
1. There are several alternative ways to tell cx_Oracle where your Oracle Client
libraries are, see :ref:`initialization`.
* With Oracle Instant Client you can use :meth:`~cx_Oracle.init_oracle_client()`
in your application, for example:
.. code-block:: python
import cx_Oracle
cx_Oracle.init_oracle_client(lib_dir=r"C:\oracle\instantclient_19_11")
Note a 'raw' string is used because backslashes occur in the path.
* Alternatively, add the Oracle Instant Client directory to the ``PATH``
environment variable. The directory must occur in ``PATH`` before any
other Oracle directories. Restart any open command prompt windows.
* Another way to set ``PATH`` is to use a batch file that sets it before Python
is executed, for example::
REM mypy.bat
SET PATH=C:\oracle\instantclient_19_9;%PATH%
python %*
Invoke this batch file every time you want to run Python.
2. If you use optional Oracle configuration files such as ``tnsnames.ora``,
``sqlnet.ora`` or ``oraaccess.xml`` with Instant Client, then put the files
in an accessible directory, for example in
``C:\oracle\your_config_dir``. Then use:
.. code-block:: python
import cx_Oracle
cx_Oracle.init_oracle_client(lib_dir=r"C:\oracle\instantclient_19_11",
config_dir=r"C:\oracle\your_config_dir")
Or set the environment variable ``TNS_ADMIN`` to that directory name.
Alternatively, put the files in a ``network\admin`` subdirectory of
Instant Client, for example in
``C:\oracle\instantclient_19_11\network\admin``. This is the default
Oracle configuration directory for executables linked with this
Instant Client.
Local Database or Full Oracle Client
++++++++++++++++++++++++++++++++++++
cx_Oracle applications can use Oracle Client 19, 18, 12, or 11.2
libraries libraries from a local Oracle Database or full Oracle
Client.
The Oracle libraries must be either 32-bit or 64-bit, matching your
Python architecture.
1. Set the environment variable ``PATH`` to include the path that contains
``OCI.DLL``, if it is not already set.
Restart any open command prompt windows.
2. Optional Oracle configuration files such as ``tnsnames.ora``,
``sqlnet.ora`` or ``oraaccess.xml`` can be placed in the
``network\admin`` subdirectory of the Oracle Database software
installation.
Alternatively, pass ``config_dir`` to :meth:`~cx_Oracle.init_oracle_client()`
as shown in the previous section, or set ``TNS_ADMIN`` to the directory name.
Installing cx_Oracle on macOS (Intel x86)
=========================================
.. note::
**cx_Oracle has a major new release under a new name and homepage**
`python-oracledb <https://oracle.github.io/python-oracledb/>`__.
**New projects should install python-oracledb instead of cx_Oracle.**
Install Python
--------------
Make sure you are not using the bundled Python. This has restricted
entitlements and will fail to load Oracle client libraries. Instead use
`Homebrew <https://brew.sh>`__ or `Python.org
<https://www.python.org/downloads>`__.
A C compiler is needed, for example Xcode and its command line tools.
Install cx_Oracle
-----------------
Use Python's `Pip <https://pip.readthedocs.io/en/latest/installing/>`__
package to install cx_Oracle from `PyPI
<https://pypi.org/project/cx-Oracle/>`__:
.. code-block:: shell
export ARCHFLAGS="-arch x86_64"
python -m pip install cx_Oracle --upgrade
The ``--user`` option may be useful, if you don't have permission to write to
system directories:
.. code-block:: shell
python -m pip install cx_Oracle --upgrade --user
If you are behind a proxy, add a proxy server to the command, for example add
``--proxy=http://proxy.example.com:80``
The source will be downloaded, compiled, and the resulting binary
installed.
Install Oracle Instant Client
-----------------------------
Oracle Instant Client provides the network connectivity for accessing Oracle
Database.
Manual Installation
+++++++++++++++++++
* Download the **Basic** 64-bit DMG from `Oracle
<https://www.oracle.com/database/technologies/instant-client/macos-intel-x86-downloads.html>`__.
* In Finder, double click on the DMG to mount it.
* Open a terminal window and run the install script in the mounted package, for example:
.. code-block:: shell
/Volumes/instantclient-basic-macos.x64-19.8.0.0.0dbru/install_ic.sh
This copies the contents to ``$HOME/Downloads/instantclient_19_8``.
Applications may not have access to the ``Downloads`` directory, so you
should move Instant Client somewhere convenient.
* In Finder, eject the mounted Instant Client package.
If you have multiple Instant Client DMG packages mounted, you only need to run
``install_ic.sh`` once. It will copy all mounted Instant Client DMG packages at
the same time.
Scripted Installation
+++++++++++++++++++++
Instant Client installation can alternatively be scripted, for example:
.. code-block:: shell
cd $HOME/Downloads
curl -O https://download.oracle.com/otn_software/mac/instantclient/198000/instantclient-basic-macos.x64-19.8.0.0.0dbru.dmg
hdiutil mount instantclient-basic-macos.x64-19.8.0.0.0dbru.dmg
/Volumes/instantclient-basic-macos.x64-19.8.0.0.0dbru/install_ic.sh
hdiutil unmount /Volumes/instantclient-basic-macos.x64-19.8.0.0.0dbru
The Instant Client directory will be ``$HOME/Downloads/instantclient_19_8``.
Applications may not have access to the ``Downloads`` directory, so you should
move Instant Client somewhere convenient.
Configure Oracle Instant Client
-------------------------------
1. Call :meth:`~cx_Oracle.init_oracle_client()` once in your application:
.. code-block:: python
import cx_Oracle
cx_Oracle.init_oracle_client(lib_dir="/Users/your_username/Downloads/instantclient_19_8")
2. If you use optional Oracle configuration files such as ``tnsnames.ora``,
``sqlnet.ora`` or ``oraaccess.xml`` with Oracle Instant Client, then put the
files in an accessible directory, for example in
``/Users/your_username/oracle/your_config_dir``. Then use:
.. code-block:: python
import cx_Oracle
cx_Oracle.init_oracle_client(lib_dir="/Users/your_username/Downloads/instantclient_19_8",
config_dir="/Users/your_username/oracle/your_config_dir")
Or set the environment variable ``TNS_ADMIN`` to that directory name.
Alternatively, put the files in the ``network/admin`` subdirectory of Oracle
Instant Client, for example in
``/Users/your_username/Downloads/instantclient_19_8/network/admin``. This is the
default Oracle configuration directory for executables linked with this
Instant Client.
Linux Containers
================
Sample Dockerfiles are on `GitHub
<https://github.com/oracle/docker-images/tree/main/OracleLinuxDevelopers>`__.
Pre-built images for Python and cx_Oracle are in the `GitHub Container Registry
<https://github.com/orgs/oracle/packages>`__. These are easily used. For
example, to pull an Oracle Linux 8 image with Python 3.6 and cx_Oracle,
execute::
docker pull ghcr.io/oracle/oraclelinux7-python:3.6-oracledb
Installing cx_Oracle without Internet Access
============================================
To install cx_Oracle on a computer that is not connected to the
internet, download the appropriate cx_Oracle file from `PyPI
<https://pypi.org/project/cx-Oracle/#files>`__. Transfer this file to
the offline computer and install it with::
python -m pip install "<file_name>"
Then follow the general cx_Oracle platform installation instructions
to install Oracle client libraries.
Install Using GitHub
====================
In order to install using the source on GitHub, use the following commands::
git clone https://github.com/oracle/python-cx_Oracle.git cx_Oracle
cd cx_Oracle
git submodule init
git submodule update
python setup.py install
Note that if you download a source zip file directly from GitHub then
you will also need to download an `ODPI-C
<https://github.com/oracle/odpi>`__ source zip file and extract it
inside the directory called "odpi".
cx_Oracle source code is also available from opensource.oracle.com. This can
be cloned with::
git clone git://opensource.oracle.com/git/oracle/python-cx_Oracle.git cx_Oracle
cd cx_Oracle
git submodule init
git submodule update
Install Using Source from PyPI
==============================
The source package can be downloaded manually from
`PyPI <https://pypi.org/project/cx-Oracle/>`__ and extracted, after
which the following commands should be run::
python setup.py build
python setup.py install
Upgrading from Older Versions
=============================
Review the :ref:`release notes <releasenotes>` and :ref:`Deprecations
<deprecations>` for changes. Modify affected code.
If you are upgrading from cx_Oracle 7 note these changes:
- The default character set used by cx_Oracle 8 is now "UTF-8". Also, the
character set component of the ``NLS_LANG`` environment variable is
ignored. If you need to change the character set, then pass ``encoding``
and ``nendcoding`` parameters when creating a connection or connection
pool. See :ref:`globalization`.
- Any uses of ``type(var)`` need to be changed to ``var.type``.
- Any uses of ``var.type is not None`` need to be changed to
``isinstance(var.type, cx_Oracle.ObjectType)``
- Note that ``TIMESTAMP WITH TIME ZONE`` columns will now be reported as
:data:`cx_Oracle.DB_TYPE_TIMESTAMP_TZ` instead of
:data:`cx_Oracle.TIMESTAMP` in :data:`Cursor.description`.
- Note that ``TIMESTAMP WITH LOCAL TIME ZONE`` columns will now be reported
as :data:`cx_Oracle.DB_TYPE_TIMESTAMP_LTZ` instead of
:data:`cx_Oracle.TIMESTAMP` in :data:`Cursor.description`.
- Note that ``BINARY_FLOAT`` columns will now be reported as
:data:`cx_Oracle.DB_TYPE_BINARY_FLOAT` instead of
:data:`cx_Oracle.NATIVE_DOUBLE` in :data:`Cursor.description`.
If you are upgrading from cx_Oracle 5 note these installation changes:
- When using Oracle Instant Client, you should not set ``ORACLE_HOME``.
- On Linux, cx_Oracle 6 and higher no longer uses Instant Client RPMs
automatically. You must set ``LD_LIBRARY_PATH`` or use ``ldconfig`` to
locate the Oracle client library.
- PyPI no longer allows Windows installers or Linux RPMs to be
hosted. Use the supplied cx_Oracle Wheels instead, or use RPMs
from Oracle, see :ref:`oraclelinux`.
.. _python2:
Installing cx_Oracle in Python 2
================================
cx_Oracle 7.3 was the last version with support for Python 2.
If you install cx_Oracle in Python 2 using the commands provided above, then
cx_Oracle 7.3 will be installed. This is equivalent to using a command like::
python -m pip install cx_Oracle==7.3 --upgrade --user
For other installation options such as installing through a proxy, see
instructions above. Make sure the Oracle Client libraries are in the system
library search path because cx_Oracle 7 does not support the
:meth:`cx_Oracle.init_oracle_client()` method and does not support loading the
Oracle Client libraries from the directory containing the cx_Oracle module
binary.
Installing cx_Oracle 5.3
========================
If you require cx_Oracle 5.3, download a Windows installer from `PyPI
<https://pypi.org/project/cx-Oracle/>`__ or use ``python -m pip
install cx-oracle==5.3`` to install from source.
Very old versions of cx_Oracle can be found in the files section at
`SourceForce <https://sourceforge.net/projects/cx-oracle/files/>`__.
Troubleshooting
===============
If installation fails:
- Use option ``-v`` with pip. Review your output and logs. Try to install
using a different method. **Google anything that looks like an error.**
Try some potential solutions.
- Was there a network connection error? Do you need to set the
environment variables ``http_proxy`` and/or ``https_proxy``? Or
try ``pip install --proxy=http://proxy.example.com:80 cx_Oracle
--upgrade``?
- If upgrading gave no errors but the old version is still
installed, try ``pip install cx_Oracle --upgrade
--force-reinstall``
- If you do not have access to modify your system version of
Python, can you use ``pip install cx_Oracle --upgrade --user``
or venv?
- Do you get the error "``No module named pip``"? The pip module is builtin
to Python but is sometimes removed by the OS. Use the venv module
(builtin to Python 3.x) or virtualenv module instead.
- Do you get the error "``fatal error: dpi.h: No such file or directory``"
when building from source code? Ensure that your source installation has
a subdirectory called "odpi" containing files. If missing, review the
section on `Install Using GitHub`_.
If using cx_Oracle fails:
- Do you get the error "``DPI-1047: Oracle Client library cannot be
loaded``"?
- On Windows and macOS, try using :meth:`~cx_Oracle.init_oracle_client()`.
See :ref:`usinginitoracleclient`.
- Check that Python and your Oracle Client libraries are both 64-bit, or
both 32-bit. The ``DPI-1047`` message will tell you whether the 64-bit
or 32-bit Oracle Client is needed for your Python.
- Set the environment variable ``DPI_DEBUG_LEVEL`` to 64 and restart
cx_Oracle. The trace messages will show how and where cx_Oracle is
looking for the Oracle Client libraries.
At a Windows command prompt, this could be done with::
set DPI_DEBUG_LEVEL=64
On Linux and macOS, you might use::
export DPI_DEBUG_LEVEL=64
- On Windows, if you used :meth:`~cx_Oracle.init_oracle_client()` and have
a full database installation, make sure this database is the `currently
configured database
<https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-33D575DD-47FF-42B1-A82F-049D3F2A8791>`__.
- On Windows, if you are not using
:meth:`~cx_Oracle.init_oracle_client()`, then restart your command prompt
and use ``set PATH`` to check the environment variable has the correct
Oracle Client listed before any other Oracle directories.
- On Windows, use the ``DIR`` command to verify that ``OCI.DLL`` exists in
the directory passed to ``init_oracle_client()`` or set in ``PATH``.
- On Windows, check that the correct `Windows Redistributables
<https://oracle.github.io/odpi/doc/installation.html#windows>`__ have
been installed.
- On Linux, check the ``LD_LIBRARY_PATH`` environment variable contains
the Oracle Client library directory. If you are using Oracle Instant
Client, a preferred alternative is to ensure a file in the
``/etc/ld.so.conf.d`` directory contains the path to the Instant Client
directory, and then run ``ldconfig``.
- On macOS, make sure you are not using the bundled Python (use `Homebrew
<https://brew.sh>`__ or `Python.org
<https://www.python.org/downloads>`__ instead). If you are not using
:meth:`~cx_Oracle.init_oracle_client()`, then put the Oracle Instant
Client libraries in ``~/lib`` or ``/usr/local/lib``.
- If you got "``DPI-1072: the Oracle Client library version is
unsupported``", then review the installation requirements. cx_Oracle
needs Oracle client libraries 11.2 or later. Note that version 19 is not
supported on Windows 7. Similar steps shown above for ``DPI-1047`` may
help.
- If you have multiple versions of Python installed, make sure you are
using the correct python and pip (or python3 and pip3) executables.

View File

@ -0,0 +1,145 @@
.. _introduction:
*************************
Introduction to cx_Oracle
*************************
cx_Oracle is a Python extension module that enables Python access to Oracle
Database. It conforms to the `Python Database API v2.0 Specification
<https://www.python.org/dev/peps/pep-0249/>`__ with a considerable number of
additions and a couple of exclusions.
.. note::
**cx_Oracle has a major new release under a new name and homepage**
`python-oracledb <https://oracle.github.io/python-oracledb/>`__.
**New projects should install python-oracledb instead of cx_Oracle.**
Architecture
------------
Python programs call cx_Oracle functions. Internally cx_Oracle dynamically
loads Oracle Client libraries to access Oracle Database. The database can be on
the same machine as Python, or it can be remote.
.. _archfig:
.. figure:: /images/cx_Oracle_arch.png
cx_Oracle Architecture
cx_Oracle is typically installed from `PyPI
<https://pypi.org/project/cx-Oracle/>`__ using `pip
<https://pip.pypa.io/en/latest/installing/>`__. The Oracle Client libraries
need to be installed separately. The libraries can be from an installation of
`Oracle Instant Client
<https://www.oracle.com/database/technologies/instant-client.html>`__, from a
full Oracle Client installation, or even from an Oracle Database installation
(if Python is running on the same machine as the database). Oracles standard
client-server version interoperability allows connection to both older and
newer databases from different Client library versions, see :ref:`cx_Oracle
Installation <installation>`.
Some behaviors of the Oracle Client libraries can optionally be configured with
an ``oraaccess.xml`` file, for example to enable auto-tuning of a statement
cache. See :ref:`optclientfiles`.
The Oracle Net layer can optionally be configured with files such as
``tnsnames.ora`` and ``sqlnet.ora``, for example to enable :ref:`network
encryption <netencrypt>`. See :ref:`optnetfiles`.
Oracle environment variables that are set before cx_Oracle first creates a
database connection will affect cx_Oracle behavior. Optional variables include
NLS_LANG, NLS_DATE_FORMAT and TNS_ADMIN. See :ref:`envset`.
Features
--------
The cx_Oracle feature highlights are:
* Easy installation from PyPI
* Support for multiple Oracle Client and Database versions
* Execution of SQL and PL/SQL statements
* Extensive Oracle data type support, including large objects (CLOB and
BLOB) and binding of SQL objects
* Connection management, including connection pooling
* Oracle Database High Availability features
* Full use of Oracle Network Service infrastructure, including encrypted
network traffic and security features
A complete list of supported features can be seen `here
<https://oracle.github.io/python-cx_Oracle/index.html#features>`_.
Getting Started
---------------
Install cx_Oracle using the :ref:`installation <installation>` steps.
Create a script ``query.py`` as shown below:
.. code-block:: python
# query.py
import cx_Oracle
# Establish the database connection
connection = cx_Oracle.connect(user="hr", password=userpwd,
dsn="dbhost.example.com/orclpdb1")
# Obtain a cursor
cursor = connection.cursor()
# Data for binding
manager_id = 145
first_name = "Peter"
# Execute the query
sql = """SELECT first_name, last_name
FROM employees
WHERE manager_id = :mid AND first_name = :fn"""
cursor.execute(sql, mid=manager_id, fn=first_name)
# Loop over the result set
for row in cursor:
print(row)
This uses Oracle's `sample HR schema
<https://github.com/oracle/db-sample-schemas>`__.
Simple :ref:`connection <connhandling>` to the database requires a username,
password and connection string. Locate your Oracle Database `user name and
password <https://www.youtube.com/watch?v=WDJacg0NuLo>`_ and the database
:ref:`connection string <connstr>`, and use them in ``query.py``. For
cx_Oracle, the connection string is commonly of the format
``hostname/servicename``, using the host name where the database is running and
the Oracle Database service name of the database instance.
The :ref:`cursor <cursorobj>` is the object that allows statements to be
executed and results (if any) fetched.
The data values in ``managerId`` and ``firstName`` are 'bound' to the statement
placeholder 'bind variables' ``:mid`` and ``:fn`` when the statement is
executed. This separates the statement text from the data, which helps avoid
SQL Injection security risks. :ref:`Binding <bind>` is also important for
performance and scalability.
The cursor allows rows to be iterated over and displayed.
Run the script::
python query.py
The output is::
('Peter', 'Hall')
('Peter', 'Tucker')
Examples and Tutorials
----------------------
Runnable examples are in the `GitHub samples directory
<https://github.com/oracle/python-cx_Oracle/tree/main/samples>`__. A `Python
cx_Oracle tutorial
<https://oracle.github.io/python-cx_Oracle/samples/tutorial/Python-and-Oracle-Database-Scripting-for-the-Future.html>`__
is also available.

View File

@ -0,0 +1,317 @@
.. _jsondatatype:
*******************************
Working with the JSON Data Type
*******************************
.. note::
**cx_Oracle has a major new release under a new name and homepage**
`python-oracledb <https://oracle.github.io/python-oracledb/>`__.
**New projects should install python-oracledb instead of cx_Oracle.**
Native support for JSON data was introduced in Oracle Database 12c. You can
use JSON with relational database features, including transactions, indexing,
declarative querying, and views. You can project JSON data relationally,
making it available for relational processes and tools. Also see
:ref:`Simple Oracle Document Access (SODA) <sodausermanual>`, which allows
access to JSON documents through a set of NoSQL-style APIs.
Prior to Oracle Database 21, JSON in relational tables is stored as BLOB, CLOB
or VARCHAR2 data, allowing easy access with cx_Oracle. Oracle Database 21
introduced a dedicated JSON data type with a new `binary storage format
<https://blogs.oracle.com/jsondb/osonformat>`__ that improves performance and
functionality. To use the new dedicated JSON type, the Oracle Database and
Oracle Client libraries must be version 21, or later. Also cx_Oracle must be
8.1, or later.
For more information about using JSON in Oracle Database see the
`Database JSON Developer's Guide
<https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=ADJSN>`__.
In Oracle Database 21, to create a table with a column called ``JSON_DATA`` for
JSON data:
.. code-block:: sql
create table customers (
id integer not null primary key,
json_data json
);
For older Oracle Database versions the syntax is:
.. code-block:: sql
create table customers (
id integer not null primary key,
json_data blob check (json_data is json)
);
The check constraint with the clause ``IS JSON`` ensures only JSON data is
stored in that column.
The older syntax can still be used in Oracle Database 21, however the
recommendation is to move to the new JSON type. With the old syntax, the
storage can be BLOB, CLOB or VARCHAR2. Of these, BLOB is preferred to avoid
character set conversion overheads.
Using Oracle Database 21 and Oracle Client 21 with cx_Oracle 8.1 (or later),
you can insert by binding as shown below:
.. code-block:: python
import datetime
json_data = [
2.78,
True,
'Ocean Beach',
b'Some bytes',
{'keyA': 1, 'KeyB': 'Melbourne'},
datetime.date.today()
]
var = cursor.var(cx_Oracle.DB_TYPE_JSON)
var.setvalue(0, json_data)
cursor.execute("insert into customers values (:1, :2)", [123, var])
# or these two lines can replace the three previous lines
cursor.setinputsizes(None, cx_Oracle.DB_TYPE_JSON)
cursor.execute("insert into customers values (:1, :2)", [123, json_data])
Fetching with:
.. code-block:: python
for row in cursor.execute("SELECT c.json_data FROM customers c"):
print(row)
gives output like::
([Decimal('2.78'), True, 'Ocean Beach',
b'Some bytes',
{'keyA': Decimal('1'), 'KeyB': 'Melbourne'},
datetime.datetime(2020, 12, 2, 0, 0)],)
With the older BLOB storage, or to insert JSON strings, use:
.. code-block:: python
import json
customer_data = dict(name="Rod", dept="Sales", location="Germany")
cursor.execute("insert into customers (id, json_data) values (:1, :2)",
[1, json.dumps(customer_data)])
IN Bind Type Mapping
====================
When binding to a JSON value, the type parameter for the variable must be
specified as :data:`cx_Oracle.DB_TYPE_JSON`. Python values are converted to
JSON values as shown in the following table. The 'SQL Equivalent' syntax can
be used in SQL INSERT and UPDATE statements if specific attribute types are
needed but there is no direct mapping from Python.
.. list-table::
:header-rows: 1
:widths: 1 1 1
:align: left
* - Python Type or Value
- JSON Attribute Type or Value
- SQL Equivalent Example
* - None
- null
- NULL
* - True
- true
- n/a
* - False
- false
- n/a
* - int
- NUMBER
- json_scalar(1)
* - float
- NUMBER
- json_scalar(1)
* - decimal.Decimal
- NUMBER
- json_scalar(1)
* - str
- VARCHAR2
- json_scalar('String')
* - datetime.date
- TIMESTAMP
- json_scalar(to_timestamp('2020-03-10', 'YYYY-MM-DD'))
* - datetime.datetime
- TIMESTAMP
- json_scalar(to_timestamp('2020-03-10', 'YYYY-MM-DD'))
* - bytes
- RAW
- json_scalar(utl_raw.cast_to_raw('A raw value'))
* - list
- Array
- json_array(1, 2, 3 returning json)
* - dict
- Object
- json_object(key 'Fred' value json_scalar(5), key 'George' value json_scalar('A string') returning json)
* - n/a
- CLOB
- json_scalar(to_clob('A short CLOB'))
* - n/a
- BLOB
- json_scalar(to_blob(utl_raw.cast_to_raw('A short BLOB')))
* - n/a
- DATE
- json_scalar(to_date('2020-03-10', 'YYYY-MM-DD'))
* - n/a
- INTERVAL YEAR TO MONTH
- json_scalar(to_yminterval('+5-9'))
* - n/a
- INTERVAL DAY TO SECOND
- json_scalar(to_dsinterval('P25DT8H25M'))
* - n/a
- BINARY_DOUBLE
- json_scalar(to_binary_double(25))
* - n/a
- BINARY_FLOAT
- json_scalar(to_binary_float(15.5))
An example of creating a CLOB attribute with key ``mydocument`` in a JSON column
using SQL is:
.. code-block:: python
cursor.execute("""
insert into mytab (myjsoncol) values
(json_object(key 'mydocument' value json_scalar(to_clob(:b))
returning json))""",
['A short CLOB'])
When `mytab` is queried in cx_Oracle, the CLOB data will be returned as a
Python string, as shown by the following table. Output might be like::
{mydocument: 'A short CLOB'}
Query and OUT Bind Type Mapping
===============================
When getting Oracle Database 21 JSON values from the database, the following
attribute mapping occurs:
.. list-table::
:header-rows: 1
:widths: 1 1
:align: left
* - Database JSON Attribute Type or Value
- Python Type or Value
* - null
- None
* - false
- False
* - true
- True
* - NUMBER
- decimal.Decimal
* - VARCHAR2
- str
* - RAW
- bytes
* - CLOB
- str
* - BLOB
- bytes
* - DATE
- datetime.datetime
* - TIMESTAMP
- datetime.datetime
* - INTERVAL YEAR TO MONTH
- not supported
* - INTERVAL DAY TO SECOND
- datetime.timedelta
* - BINARY_DOUBLE
- float
* - BINARY_FLOAT
- float
* - Arrays
- list
* - Objects
- dict
SQL/JSON Path Expressions
=========================
Oracle Database provides SQL access to JSON data using SQL/JSON path
expressions. A path expression selects zero or more JSON values that match, or
satisfy, it. Path expressions can use wildcards and array ranges. A simple
path expression is ``$.friends`` which is the value of the JSON field
``friends``.
For example, the previously created ``customers`` table with JSON column
``json_data`` can be queried like:
.. code-block:: sql
select c.json_data.location FROM customers c
With the JSON ``'{"name":"Rod","dept":"Sales","location":"Germany"}'`` stored
in the table, the queried value would be ``Germany``.
The JSON_EXISTS functions tests for the existence of a particular value within
some JSON data. To look for JSON entries that have a ``location`` field:
.. code-block:: python
for blob, in cursor.execute("""
select json_data
from customers
where json_exists(json_data, '$.location')"""):
data = json.loads(blob.read())
print(data)
This query might display::
{'name': 'Rod', 'dept': 'Sales', 'location': 'Germany'}
The SQL/JSON functions ``JSON_VALUE`` and ``JSON_QUERY`` can also be used.
Note that the default error-handling behavior for these functions is
``NULL ON ERROR``, which means that no value is returned if an error occurs.
To ensure that an error is raised, use ``ERROR ON ERROR``.
For more information, see `SQL/JSON Path Expressions
<https://www.oracle.com/pls/topic/lookup?ctx=dblatest&
id=GUID-2DC05D71-3D62-4A14-855F-76E054032494>`__
in the Oracle JSON Developer's Guide.
Accessing Relational Data as JSON
=================================
In Oracle Database 12.2, or later, the `JSON_OBJECT
<https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-1EF347AE-7FDA-4B41-AFE0-DD5A49E8B370>`__
function is a great way to convert relational table data to JSON:
.. code-block:: python
cursor.execute("""
select json_object('deptId' is d.department_id, 'name' is d.department_name) department
from departments d
where department_id < :did
order by d.department_id""",
[50]);
for row in cursor:
print(row)
This produces::
('{"deptId":10,"name":"Administration"}',)
('{"deptId":20,"name":"Marketing"}',)
('{"deptId":30,"name":"Purchasing"}',)
('{"deptId":40,"name":"Human Resources"}',)

View File

@ -0,0 +1,207 @@
.. _lobdata:
************************
Using CLOB and BLOB Data
************************
.. note::
**cx_Oracle has a major new release under a new name and homepage**
`python-oracledb <https://oracle.github.io/python-oracledb/>`__.
**New projects should install python-oracledb instead of cx_Oracle.**
Oracle Database uses :ref:`lobobj` to store large data such as text, images,
videos and other multimedia formats. The maximum size of a LOB is limited to
the size of the tablespace storing it.
There are four types of LOB (large object):
* BLOB - Binary Large Object, used for storing binary data. cx_Oracle uses
the type :attr:`cx_Oracle.DB_TYPE_BLOB`.
* CLOB - Character Large Object, used for string strings in the database
character set format. cx_Oracle uses the type
:attr:`cx_Oracle.DB_TYPE_CLOB`.
* NCLOB - National Character Large Object, used for string strings in the
national character set format. cx_Oracle uses the type
:attr:`cx_Oracle.DB_TYPE_NCLOB`.
* BFILE - External Binary File, used for referencing a file stored on the
host operating system outside of the database. cx_Oracle uses the type
:attr:`cx_Oracle.DB_TYPE_BFILE`.
LOBs can be streamed to, and from, Oracle Database.
LOBs up to 1 GB in length can be also be handled directly as strings or bytes in
cx_Oracle. This makes LOBs easy to work with, and has significant performance
benefits over streaming. However it requires the entire LOB data to be present
in Python memory, which may not be possible.
See `GitHub <https://github.com/oracle/python-cx_Oracle/tree/main/samples>`__ for LOB examples.
Simple Insertion of LOBs
------------------------
Consider a table with CLOB and BLOB columns:
.. code-block:: sql
CREATE TABLE lob_tbl (
id NUMBER,
c CLOB,
b BLOB
);
With cx_Oracle, LOB data can be inserted in the table by binding strings or
bytes as needed:
.. code-block:: python
with open('example.txt', 'r') as f:
text_data = f.read()
with open('image.png', 'rb') as f:
img_data = f.read()
cursor.execute("""
insert into lob_tbl (id, c, b)
values (:lobid, :clobdata, :blobdata)""",
lobid=10, clobdata=text_data, blobdata=img_data)
Note that with this approach, LOB data is limited to 1 GB in size.
.. _directlobs:
Fetching LOBs as Strings and Bytes
----------------------------------
CLOBs and BLOBs smaller than 1 GB can queried from the database directly as
strings and bytes. This can be much faster than streaming.
A :attr:`Connection.outputtypehandler` or :attr:`Cursor.outputtypehandler` needs
to be used as shown in this example:
.. code-block:: python
def output_type_handler(cursor, name, default_type, size, precision, scale):
if default_type == cx_Oracle.DB_TYPE_CLOB:
return cursor.var(cx_Oracle.DB_TYPE_LONG, arraysize=cursor.arraysize)
if default_type == cx_Oracle.DB_TYPE_BLOB:
return cursor.var(cx_Oracle.DB_TYPE_LONG_RAW, arraysize=cursor.arraysize)
id_val = 1
text_data = "The quick brown fox jumps over the lazy dog"
binary_data = b"Some binary data"
cursor.execute("insert into lob_tbl (id, c, b) values (:1, :2, :3)",
[id_val, text_data, binary_data])
connection.outputtypehandler = output_type_handler
cursor.execute("select c, b from lob_tbl where id = :1", [id_val])
clob_data, blob_data = cursor.fetchone()
print("CLOB length:", len(clob_data))
print("CLOB data:", clob_data)
print("BLOB length:", len(blob_data))
print("BLOB data:", blob_data)
This displays::
CLOB length: 43
CLOB data: The quick brown fox jumps over the lazy dog
BLOB length: 16
BLOB data: b'Some binary data'
Streaming LOBs (Read)
---------------------
Without the output type handler, the CLOB and BLOB values are fetched as
:ref:`LOB objects<lobobj>`. The size of the LOB object can be obtained by
calling :meth:`LOB.size()` and the data can be read by calling
:meth:`LOB.read()`:
.. code-block:: python
id_val = 1
text_data = "The quick brown fox jumps over the lazy dog"
binary_data = b"Some binary data"
cursor.execute("insert into lob_tbl (id, c, b) values (:1, :2, :3)",
[id_val, text_data, binary_data])
cursor.execute("select b, c from lob_tbl where id = :1", [id_val])
b, c = cursor.fetchone()
print("CLOB length:", c.size())
print("CLOB data:", c.read())
print("BLOB length:", b.size())
print("BLOB data:", b.read())
This approach produces the same results as the previous example but it will
perform more slowly because it requires more :ref:`round-trips <roundtrips>` to
Oracle Database and has higher overhead. It is needed, however, if the LOB data
cannot be fetched as one block of data from the server.
To stream the BLOB column, the :meth:`LOB.read()` method can be called
repeatedly until all of the data has been read, as shown below:
.. code-block:: python
cursor.execute("select b from lob_tbl where id = :1", [10])
blob, = cursor.fetchone()
offset = 1
num_bytes_in_chunk = 65536
with open("image.png", "wb") as f:
while True:
data = blob.read(offset, num_bytes_in_chunk)
if data:
f.write(data)
if len(data) < num_bytes_in_chunk:
break
offset += len(data)
Streaming LOBs (Write)
----------------------
If a row containing a LOB is being inserted or updated, and the quantity of
data that is to be inserted or updated cannot fit in a single block of data,
the data can be streamed using the method :meth:`LOB.write()` instead as shown
in the following code:
.. code-block:: python
id_val = 9
lob_var = cursor.var(cx_Oracle.DB_TYPE_BLOB)
cursor.execute("""
insert into lob_tbl (id, b)
values (:1, empty_blob())
returning b into :2""", [id_val, lob_var])
blob, = lobVar.getvalue()
offset = 1
num_bytes_in_chunk = 65536
with open("image.png", "rb") as f:
while True:
data = f.read(num_bytes_in_chunk)
if data:
blob.write(data, offset)
if len(data) < num_bytes_in_chunk:
break
offset += len(data)
connection.commit()
Temporary LOBs
--------------
All of the examples shown thus far have made use of permanent LOBs. These are
LOBs that are stored in the database. Oracle also supports temporary LOBs that
are not stored in the database but can be used to pass large quantities of
data. These LOBs use space in the temporary tablespace until all variables
referencing them go out of scope or the connection in which they are created is
explicitly closed.
When calling PL/SQL procedures with data that exceeds 32,767 bytes in length,
cx_Oracle automatically creates a temporary LOB internally and passes that
value through to the procedure. If the data that is to be passed to the
procedure exceeds that which can fit in a single block of data, however, you
can use the method :meth:`Connection.createlob()` to create a temporary LOB.
This LOB can then be read and written just like in the examples shown above for
persistent LOBs.

View File

@ -0,0 +1,388 @@
.. _plsqlexecution:
****************
PL/SQL Execution
****************
.. note::
**cx_Oracle has a major new release under a new name and homepage**
`python-oracledb <https://oracle.github.io/python-oracledb/>`__.
**New projects should install python-oracledb instead of cx_Oracle.**
PL/SQL stored procedures, functions and anonymous blocks can be called from
cx_Oracle.
.. _plsqlproc:
PL/SQL Stored Procedures
------------------------
The :meth:`Cursor.callproc()` method is used to call PL/SQL procedures.
If a procedure with the following definition exists:
.. code-block:: sql
create or replace procedure myproc (
a_Value1 number,
a_Value2 out number
) as
begin
a_Value2 := a_Value1 * 2;
end;
then the following Python code can be used to call it:
.. code-block:: python
out_val = cursor.var(int)
cursor.callproc('myproc', [123, out_val])
print(out_val.getvalue()) # will print 246
Calling :meth:`Cursor.callproc()` actually generates an anonymous PL/SQL block
as shown below, which is then executed:
.. code-block:: python
cursor.execute("begin myproc(:1,:2); end;", [123, out_val])
See :ref:`bind` for information on binding.
.. _plsqlfunc:
PL/SQL Stored Functions
-----------------------
The :meth:`Cursor.callfunc()` method is used to call PL/SQL functions.
The ``returnType`` parameter for :meth:`~Cursor.callfunc()` is
expected to be a Python type, one of the :ref:`cx_Oracle types <types>` or
an :ref:`Object Type <objecttype>`.
If a function with the following definition exists:
.. code-block:: sql
create or replace function myfunc (
a_StrVal varchar2,
a_NumVal number
) return number as
begin
return length(a_StrVal) + a_NumVal * 2;
end;
then the following Python code can be used to call it:
.. code-block:: python
return_val = cursor.callfunc("myfunc", int, ["a string", 15])
print(return_val) # will print 38
A more complex example that returns a spatial (SDO) object can be seen below.
First, the SQL statements necessary to set up the example:
.. code-block:: sql
create table MyPoints (
id number(9) not null,
point sdo_point_type not null
);
insert into MyPoints values (1, sdo_point_type(125, 375, 0));
create or replace function spatial_queryfn (
a_Id number
) return sdo_point_type is
t_Result sdo_point_type;
begin
select point
into t_Result
from MyPoints
where Id = a_Id;
return t_Result;
end;
/
The Python code that will call this procedure looks as follows:
.. code-block:: python
obj_type = connection.gettype("SDO_POINT_TYPE")
cursor = connection.cursor()
return_val = cursor.callfunc("spatial_queryfn", obj_type, [1])
print(f"({return_val.X}, {return_val.Y}, {return_val.Z})")
# will print (125, 375, 0)
See :ref:`bind` for information on binding.
Anonymous PL/SQL Blocks
-----------------------
An anonymous PL/SQL block can be called as shown:
.. code-block:: python
var = cursor.var(int)
cursor.execute("""
begin
:out_val := length(:in_val);
end;""", in_val="A sample string", out_val=var)
print(var.getvalue()) # will print 15
See :ref:`bind` for information on binding.
Creating Stored Procedures and Packages
---------------------------------------
To create PL/SQL stored procedures and packages, use :meth:`Cursor.execute()`
with a SQL CREATE command.
Creation warning messages can be found from database views like USER_ERRORS.
For example, creating a procedure with an error could be like:
.. code-block:: python
with connection.cursor() as cursor:
cursor.execute("""
create or replace procedure badproc (a in number) as
begin
WRONG WRONG WRONG
end;""")
cursor.execute("""
select line, position, text
from user_errors
where name = 'BADPROC' and type = 'PROCEDURE'
order by name, type, line, position""")
errors = cursor.fetchall()
if errors:
for info in errors:
print("Error at line {} position {}:\n{}".format(*info))
else:
print("Created successfully")
The output would be::
PLS-00103: Encountered the symbol "WRONG" when expecting one of the following:
:= . ( @ % ;
Using DBMS_OUTPUT
-----------------
The standard way to print output from PL/SQL is with the package `DBMS_OUTPUT
<https://www.oracle.com/pls/topic/lookup?ctx=dblatest&
id=GUID-C1400094-18D5-4F36-A2C9-D28B0E12FD8C>`__. Note, PL/SQL code that uses
``DBMS_OUTPUT`` runs to completion before any output is available to the user.
Also, other database connections cannot access the buffer.
To use DBMS_OUTPUT:
* Call the PL/SQL procedure ``DBMS_OUTPUT.ENABLE()`` to enable output to be
buffered for the connection.
* Execute some PL/SQL that calls ``DBMS_OUTPUT.PUT_LINE()`` to put text in the
buffer.
* Call ``DBMS_OUTPUT.GET_LINE()`` or ``DBMS_OUTPUT.GET_LINES()`` repeatedly to
fetch the text from the buffer until there is no more output.
For example:
.. code-block:: python
# enable DBMS_OUTPUT
cursor.callproc("dbms_output.enable")
# execute some PL/SQL that calls DBMS_OUTPUT.PUT_LINE
cursor.execute("""
begin
dbms_output.put_line('This is the cx_Oracle manual');
dbms_output.put_line('Demonstrating how to use DBMS_OUTPUT');
end;""")
# tune this size for your application
chunk_size = 100
# create variables to hold the output
lines_var = cursor.arrayvar(str, chunk_size)
num_lines_var = cursor.var(int)
num_lines_var.setvalue(0, chunk_size)
# fetch the text that was added by PL/SQL
while True:
cursor.callproc("dbms_output.get_lines", (lines_var, num_lines_var))
num_lines = num_lines_var.getvalue()
lines = lines_var.getvalue()[:num_lines]
for line in lines:
print(line or "")
if num_lines < chunk_size:
break
This will produce the following output::
This is the cx_Oracle manual
Demonstrating use of DBMS_OUTPUT
An alternative is to call ``DBMS_OUTPUT.GET_LINE()`` once per output line,
which may be much slower:
.. code-block:: python
text_var = cursor.var(str)
status_var = cursor.var(int)
while True:
cursor.callproc("dbms_output.get_line", (text_var, status_var))
if status_var.getvalue() != 0:
break
print(text_var.getvalue())
Implicit results
----------------
Implicit results permit a Python program to consume cursors returned by a
PL/SQL block without the requirement to use OUT REF CURSOR parameters. The
method :meth:`Cursor.getimplicitresults()` can be used for this purpose. It
requires both the Oracle Client and Oracle Database to be 12.1 or higher.
An example using implicit results is as shown:
.. code-block:: python
cursor.execute("""
declare
cust_cur sys_refcursor;
sales_cur sys_refcursor;
begin
open cust_cur for SELECT * FROM cust_table;
dbms_sql.return_result(cust_cur);
open sales_cur for SELECT * FROM sales_table;
dbms_sql.return_result(sales_cur);
end;""")
for implicit_cursor in cursor.getimplicitresults():
for row in implicit_cursor:
print(row)
Data from both the result sets are returned::
(1, 'Tom')
(2, 'Julia')
(1000, 1, 'BOOKS')
(2000, 2, 'FURNITURE')
.. _ebr:
Edition-Based Redefinition (EBR)
--------------------------------
Oracle Database's `Edition-Based Redefinition
<https://www.oracle.com/pls/topic/lookup?ctx=dblatest&
id=GUID-58DE05A0-5DEF-4791-8FA8-F04D11964906>`__ feature enables upgrading of
the database component of an application while it is in use, thereby minimizing
or eliminating down time. This feature allows multiple versions of views,
synonyms, PL/SQL objects and SQL Translation profiles to be used concurrently.
Different versions of the database objects are associated with an "edition".
The simplest way to set an edition is to pass the ``edition`` parameter to
:meth:`cx_Oracle.connect()` or :meth:`cx_Oracle.SessionPool()`:
.. code-block:: python
connection = cx_Oracle.connect(user="hr", password=userpwd,
dsn="dbhost.example.com/orclpdb1",
edition="newsales", encoding="UTF-8")
The edition could also be set by setting the environment variable
``ORA_EDITION`` or by executing the SQL statement:
.. code-block:: sql
alter session set edition = <edition name>;
Regardless of which method is used to set the edition, the value that is in use
can be seen by examining the attribute :attr:`Connection.edition`. If no value
has been set, the value will be None. This corresponds to the database default
edition ``ORA$BASE``.
Consider an example where one version of a PL/SQL function ``Discount`` is
defined in the database default edition ``ORA$BASE`` and the other version of
the same function is defined in a user created edition ``DEMO``.
.. code-block:: sql
connect <username>/<password>
-- create function using the database default edition
CREATE OR REPLACE FUNCTION Discount(price IN NUMBER) RETURN NUMBER IS
BEGIN
return price * 0.9;
END;
/
A new edition named 'DEMO' is created and the user given permission to use
editions. The use of ``FORCE`` is required if the user already contains one or
more objects whose type is editionable and that also have non-editioned
dependent objects.
.. code-block:: sql
connect system/<password>
CREATE EDITION demo;
ALTER USER <username> ENABLE EDITIONS FORCE;
GRANT USE ON EDITION demo to <username>;
The ``Discount`` function for the demo edition is as follows:
.. code-block:: sql
connect <username>/<password>
alter session set edition = demo;
-- Function for the demo edition
CREATE OR REPLACE FUNCTION Discount(price IN NUMBER) RETURN NUMBER IS
BEGIN
return price * 0.5;
END;
/
The Python application can then call the required version of the PL/SQL
function as shown:
.. code-block:: python
connection = cx_Oracle.connect(user=user, password=password,
dsn="dbhost.example.com/orclpdb1",
encoding="UTF-8")
print("Edition is:", repr(connection.edition))
cursor = connection.cursor()
discounted_price = cursor.callfunc("Discount", int, [100])
print("Price after discount is:", discounted_price)
# Use the edition parameter for the connection
connection = cx_Oracle.connect(user=user, password=password,
dsn="dbhost.example.com/orclpdb1",
edition="demo", encoding="UTF-8")
print("Edition is:", repr(connection.edition))
cursor = connection.cursor()
discounted_price = cursor.callfunc("Discount", int, [100])
print("Price after discount is:", discounted_price)
The output of the function call for the default and demo edition is as shown::
Edition is: None
Price after discount is: 90
Edition is: 'DEMO'
Price after discount is: 50

216
doc/src/user_guide/soda.rst Normal file
View File

@ -0,0 +1,216 @@
.. _sodausermanual:
************************************
Simple Oracle Document Access (SODA)
************************************
.. note::
**cx_Oracle has a major new release under a new name and homepage**
`python-oracledb <https://oracle.github.io/python-oracledb/>`__.
**New projects should install python-oracledb instead of cx_Oracle.**
Overview
========
Oracle Database Simple Oracle Document Access (SODA) allows documents to be
inserted, queried, and retrieved from Oracle Database using a set of
NoSQL-style cx_Oracle methods. Documents are generally JSON data but they can
be any data at all (including video, images, sounds, or other binary content).
Documents can be fetched from the database by key lookup or by using
query-by-example (QBE) pattern-matching.
SODA uses a SQL schema to store documents but you do not need to know SQL or
how the documents are stored. However, access via SQL does allow use of
advanced Oracle Database functionality such as analytics for reporting.
Oracle SODA implementations are also available in `Node.js
<https://oracle.github.io/node-oracledb/doc/api.html#sodaoverview>`__, `Java
<https://docs.oracle.com/en/database/oracle/simple-oracle-document-access/java/adsda/index.html>`__,
`PL/SQL <https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=ADSDP>`__,
`Oracle Call Interface
<https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-23206C89-891E-43D7-827C-5C6367AD62FD>`__
and via `REST
<https://docs.oracle.com/en/database/oracle/simple-oracle-document-access/rest/index.html>`__.
For general information on SODA, see the `SODA home page
<https://docs.oracle.com/en/database/oracle/simple-oracle-document-access/index.html>`__
and the Oracle Database `Introduction to Simple Oracle Document Access (SODA)
<https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=ADSDI>`__ manual.
For specific requirements see the cx_Oracle :ref:`SODA requirements <sodarequirements>`.
cx_Oracle uses the following objects for SODA:
* :ref:`SODA Database Object <sodadb>`: The top level object for cx_Oracle SODA
operations. This is acquired from an Oracle Database connection. A 'SODA
database' is an abstraction, allowing access to SODA collections in that
'SODA database', which then allow access to documents in those collections.
A SODA database is analogous to an Oracle Database user or schema, a
collection is analogous to a table, and a document is analogous to a table
row with one column for a unique document key, a column for the document
content, and other columns for various document attributes.
* :ref:`SODA Collection Object <sodacoll>`: Represents a collection of SODA
documents. By default, collections allow JSON documents to be stored. This
is recommended for most SODA users. However optional metadata can set
various details about a collection, such as its database storage, whether it
should track version and time stamp document components, how such components
are generated, and what document types are supported. By default, the name of
the Oracle Database table storing a collection is the same as the collection
name. Note: do not use SQL to drop the database table, since SODA metadata
will not be correctly removed. Use the :meth:`SodaCollection.drop()` method
instead.
* :ref:`SODA Document Object <sodadoc>`: Represents a document. Typically the
document content will be JSON. The document has properties including the
content, a key, timestamps, and the media type. By default, document keys
are automatically generated. See :ref:`SODA Document objects <sodadoc>` for
the forms of SodaDoc.
* :ref:`SODA Document Cursor <sodadoccur>`: A cursor object representing the
result of the :meth:`SodaOperation.getCursor()` method from a
:meth:`SodaCollection.find()` operation. It can be iterated over to access
each SodaDoc.
* :ref:`SODA Operation Object <sodaop>`: An internal object used with
:meth:`SodaCollection.find()` to perform read and write operations on
documents. Chained methods set properties on a SodaOperation object which is
then used by a terminal method to find, count, replace, or remove documents.
This is an internal object that should not be directly accessed.
SODA Examples
=============
Creating and adding documents to a collection can be done as follows:
.. code-block:: python
soda = connection.getSodaDatabase()
# create a new SODA collection; this will open an existing collection, if
# the name is already in use
collection = soda.createCollection("mycollection")
# insert a document into the collection; for the common case of a JSON
# document, the content can be a simple Python dictionary which will
# internally be converted to a JSON document
content = {'name': 'Matilda', 'address': {'city': 'Melbourne'}}
returned_doc = collection.insertOneAndGet(content)
key = returned_doc.key
print('The key of the new SODA document is: ', key)
By default, a system generated key is created when documents are inserted.
With a known key, you can retrieve a document:
.. code-block:: python
# this will return a dictionary (as was inserted in the previous code)
content = collection.find().key(key).getOne().getContent()
print(content)
You can also search for documents using query-by-example syntax:
.. code-block:: python
# Find all documents with names like 'Ma%'
print("Names matching 'Ma%'")
qbe = {'name': {'$like': 'Ma%'}}
for doc in collection.find().filter(qbe).getDocuments():
content = doc.getContent()
print(content["name"])
See the `samples directory
<https://github.com/oracle/python-cx_Oracle/tree/main/samples>`__
for runnable SODA examples.
.. _sodametadatacache:
Using the SODA Metadata Cache
=============================
SODA metadata can be cached to improve the performance of
:meth:`SodaDatabase.createCollection()` and
:meth:`SodaDatabase.openCollection()` by reducing :ref:`round-trips
<roundtrips>` to the database. Caching is available with Oracle Client 21.3 (or
later). The feature is also available in Oracle Client 19 from 19.11 onwards.
The metadata cache can be turned on when creating a connection pool with
:meth:`cx_Oracle.SessionPool()`. Each pool has its own cache:
.. code-block:: python
# Create the session pool
pool = cx_Oracle.SessionPool(user="hr", password=userpwd,
dsn="dbhost.example.com/orclpdb1",
soda_metadata_cache=True)
The cache is not available for standalone connections. Applications using these
should retain and reuse the :ref:`collection <sodacoll>` returned from
``createCollection()`` or ``openCollection()`` wherever possible, instead of
making repeated calls to those methods.
The cache is not used by ``createCollection()`` when explicitly passing
metadata. In this case, instead of using only ``createCollection()`` and
relying on its behavior of opening an existing collection like:
.. code-block:: python
mymetadata = { . . . }
collection = soda.createCollection("mycollection", mymetadata) # open existing or create new collection
collection.insertOne(mycontent)
you will find it more efficient to use logic similar to:
.. code-block:: python
collection = soda.openCollection("mycollection")
if collection is None:
mymetadata = { . . . }
collection = soda.createCollection("mycollection", mymetadata)
collection.insertOne(mycontent)
If collection metadata changes are made externally, the cache can become
invalid. If this happens, the cache can be cleared by calling
:meth:`SessionPool.reconfigure()` with ``soda_metadata_cache`` set to `False`,
or by setting the attribute :attr:`SessionPool.soda_metadata_cache` to `False`.
Use a second call to ``reconfigure()`` or set ``soda_metadata_cache`` to
re-enable the cache.
Committing SODA Work
====================
The general recommendation for SODA applications is to turn on
:attr:`~Connection.autocommit` globally:
.. code-block:: python
connection.autocommit = True
If your SODA document write operations are mostly independent of each other,
this removes the overhead of application transaction management and the need for
explicit :meth:`Connection.commit()` calls.
When deciding how to commit transactions, beware of transactional consistency
and performance requirements. If you are using individual SODA calls to insert
or update a large number of documents with individual calls, you should turn
:attr:`~Connection.autocommit` off and issue a single, explicit
:meth:`~Connection.commit()` after all documents have been processed. Also
consider using :meth:`SodaCollection.insertMany()` or
:meth:`SodaCollection.insertManyAndGet()` which have performance benefits.
If you are not autocommitting, and one of the SODA operations in your
transaction fails, then previous uncommitted operations will not be rolled back.
Your application should explicitly roll back the transaction with
:meth:`Connection.rollback()` to prevent any later commits from committing a
partial transaction.
Note:
- SODA DDL operations do not commit an open transaction the way that SQL always does for DDL statements.
- When :attr:`~Connection.autocommit` is ``True``, most SODA methods will issue a commit before successful return.
- SODA provides optimistic locking, see :meth:`SodaOperation.version()`.
- When mixing SODA and relational access, any commit or rollback on the connection will affect all work.

View File

@ -0,0 +1,812 @@
.. _sqlexecution:
*************
SQL Execution
*************
.. note::
**cx_Oracle has a major new release under a new name and homepage**
`python-oracledb <https://oracle.github.io/python-oracledb/>`__.
**New projects should install python-oracledb instead of cx_Oracle.**
Executing SQL statements is the primary way in which a Python application
communicates with Oracle Database. Statements are executed using the methods
:meth:`Cursor.execute()` or :meth:`Cursor.executemany()`. Statements include
queries, Data Manipulation Language (DML), and Data Definition Language (DDL).
A few other `specialty statements
<https://www.oracle.com/pls/topic/lookup?ctx=dblatest&
id=GUID-E1749EF5-2264-44DF-99EF-AEBEB943BED6>`__ can also be executed.
PL/SQL statements are discussed in :ref:`plsqlexecution`. Other chapters
contain information on specific data types and features. See :ref:`batchstmnt`,
:ref:`lobdata`, :ref:`jsondatatype`, and :ref:`xmldatatype`.
cx_Oracle can be used to execute individual statements, one at a time. It does
not read SQL*Plus ".sql" files. To read SQL files, use a technique like the one
in ``run_sql_script()`` in `samples/sample_env.py
<https://github.com/oracle/python-cx_Oracle/blob/main/samples/sample_env.py>`__
SQL statements should not contain a trailing semicolon (";") or forward slash
("/"). This will fail:
.. code-block:: python
cur.execute("select * from MyTable;")
This is correct:
.. code-block:: python
cur.execute("select * from MyTable")
SQL Queries
===========
Queries (statements beginning with SELECT or WITH) can only be executed using
the method :meth:`Cursor.execute()`. Rows can then be iterated over, or can be
fetched using one of the methods :meth:`Cursor.fetchone()`,
:meth:`Cursor.fetchmany()` or :meth:`Cursor.fetchall()`. There is a
:ref:`default type mapping <defaultfetchtypes>` to Python types that can be
optionally :ref:`overridden <outputtypehandlers>`.
.. IMPORTANT::
Interpolating or concatenating user data with SQL statements, for example
``cur.execute("SELECT * FROM mytab WHERE mycol = '" + myvar + "'")``, is a security risk
and impacts performance. Use :ref:`bind variables <bind>` instead. For
example, ``cur.execute("SELECT * FROM mytab WHERE mycol = :mybv", mybv=myvar)``.
.. _fetching:
Fetch Methods
-------------
After :meth:`Cursor.execute()`, the cursor is returned as a convenience. This
allows code to iterate over rows like:
.. code-block:: python
cur = connection.cursor()
for row in cur.execute("select * from MyTable"):
print(row)
Rows can also be fetched one at a time using the method
:meth:`Cursor.fetchone()`:
.. code-block:: python
cur = connection.cursor()
cur.execute("select * from MyTable")
while True:
row = cur.fetchone()
if row is None:
break
print(row)
If rows need to be processed in batches, the method :meth:`Cursor.fetchmany()`
can be used. The size of the batch is controlled by the ``numRows`` parameter,
which defaults to the value of :attr:`Cursor.arraysize`.
.. code-block:: python
cur = connection.cursor()
cur.execute("select * from MyTable")
num_rows = 10
while True:
rows = cur.fetchmany(num_rows)
if not rows:
break
for row in rows:
print(row)
If all of the rows need to be fetched, and can be contained in memory, the
method :meth:`Cursor.fetchall()` can be used.
.. code-block:: python
cur = connection.cursor()
cur.execute("select * from MyTable")
rows = cur.fetchall()
for row in rows:
print(row)
The fetch methods return data as tuples. To return results as dictionaries, see
:ref:`rowfactories`.
Closing Cursors
---------------
A cursor may be used to execute multiple statements. Once it is no longer
needed, it should be closed by calling :meth:`~Cursor.close()` in order to
reclaim resources in the database. It will be closed automatically when the
variable referencing it goes out of scope (and no further references are
retained). One other way to control the lifetime of a cursor is to use a "with"
block, which ensures that a cursor is closed once the block is completed. For
example:
.. code-block:: python
with connection.cursor() as cursor:
for row in cursor.execute("select * from MyTable"):
print(row)
This code ensures that, once the block is completed, the cursor is closed and
resources have been reclaimed by the database. In addition, any attempt to use
the variable ``cursor`` outside of the block will simply fail.
.. _querymetadata:
Query Column Metadata
---------------------
After executing a query, the column metadata such as column names and data types
can be obtained using :attr:`Cursor.description`:
.. code-block:: python
cur = connection.cursor()
cur.execute("select * from MyTable")
for column in cur.description:
print(column)
This could result in metadata like::
('ID', <class 'cx_Oracle.DB_TYPE_NUMBER'>, 39, None, 38, 0, 0)
('NAME', <class 'cx_Oracle.DB_TYPE_VARCHAR'>, 20, 20, None, None, 1)
.. _defaultfetchtypes:
Fetch Data Types
----------------
The following table provides a list of all of the data types that cx_Oracle
knows how to fetch. The middle column gives the type that is returned in the
:ref:`query metadata <querymetadata>`. The last column gives the type of
Python object that is returned by default. Python types can be changed with
:ref:`Output Type Handlers <outputtypehandlers>`.
.. list-table::
:header-rows: 1
:widths: 1 1 1
:align: left
* - Oracle Database Type
- cx_Oracle Database Type
- Default Python type
* - BFILE
- :attr:`cx_Oracle.DB_TYPE_BFILE`
- :ref:`cx_Oracle.LOB <lobobj>`
* - BINARY_DOUBLE
- :attr:`cx_Oracle.DB_TYPE_BINARY_DOUBLE`
- float
* - BINARY_FLOAT
- :attr:`cx_Oracle.DB_TYPE_BINARY_FLOAT`
- float
* - BLOB
- :attr:`cx_Oracle.DB_TYPE_BLOB`
- :ref:`cx_Oracle.LOB <lobobj>`
* - CHAR
- :attr:`cx_Oracle.DB_TYPE_CHAR`
- str
* - CLOB
- :attr:`cx_Oracle.DB_TYPE_CLOB`
- :ref:`cx_Oracle.LOB <lobobj>`
* - CURSOR
- :attr:`cx_Oracle.DB_TYPE_CURSOR`
- :ref:`cx_Oracle.Cursor <cursorobj>`
* - DATE
- :attr:`cx_Oracle.DB_TYPE_DATE`
- datetime.datetime
* - INTERVAL DAY TO SECOND
- :attr:`cx_Oracle.DB_TYPE_INTERVAL_DS`
- datetime.timedelta
* - JSON
- :attr:`cx_Oracle.DB_TYPE_JSON`
- dict, list or a scalar value [4]_
* - LONG
- :attr:`cx_Oracle.DB_TYPE_LONG`
- str
* - LONG RAW
- :attr:`cx_Oracle.DB_TYPE_LONG_RAW`
- bytes
* - NCHAR
- :attr:`cx_Oracle.DB_TYPE_NCHAR`
- str
* - NCLOB
- :attr:`cx_Oracle.DB_TYPE_NCLOB`
- :ref:`cx_Oracle.LOB <lobobj>`
* - NUMBER
- :attr:`cx_Oracle.DB_TYPE_NUMBER`
- float or int [1]_
* - NVARCHAR2
- :attr:`cx_Oracle.DB_TYPE_NVARCHAR`
- str
* - OBJECT [3]_
- :attr:`cx_Oracle.DB_TYPE_OBJECT`
- :ref:`cx_Oracle.Object <objecttype>`
* - RAW
- :attr:`cx_Oracle.DB_TYPE_RAW`
- bytes
* - ROWID
- :attr:`cx_Oracle.DB_TYPE_ROWID`
- str
* - TIMESTAMP
- :attr:`cx_Oracle.DB_TYPE_TIMESTAMP`
- datetime.datetime
* - TIMESTAMP WITH LOCAL TIME ZONE
- :attr:`cx_Oracle.DB_TYPE_TIMESTAMP_LTZ`
- datetime.datetime [2]_
* - TIMESTAMP WITH TIME ZONE
- :attr:`cx_Oracle.DB_TYPE_TIMESTAMP_TZ`
- datetime.datetime [2]_
* - UROWID
- :attr:`cx_Oracle.DB_TYPE_ROWID`
- str
* - VARCHAR2
- :attr:`cx_Oracle.DB_TYPE_VARCHAR`
- str
.. [1] If the precision and scale obtained from query column metadata indicate
that the value can be expressed as an integer, the value will be
returned as an int. If the column is unconstrained (no precision and
scale specified), the value will be returned as a float or an int
depending on whether the value itself is an integer. In all other cases
the value is returned as a float.
.. [2] The timestamps returned are naive timestamps without any time zone
information present.
.. [3] These include all user-defined types such as VARRAY, NESTED TABLE, etc.
.. [4] If the JSON is an object, then a dict is returned. If it is an array,
then a list is returned. If it is a scalar value, then that particular
scalar value is returned.
.. _outputtypehandlers:
Changing Fetched Data Types with Output Type Handlers
-----------------------------------------------------
Sometimes the default conversion from an Oracle Database type to a Python type
must be changed in order to prevent data loss or to fit the purposes of the
Python application. In such cases, an output type handler can be specified for
queries. Output type handlers do not affect values returned from
:meth:`Cursor.callfunc()` or :meth:`Cursor.callproc()`.
Output type handlers can be specified on the :attr:`connection
<Connection.outputtypehandler>` or on the :attr:`cursor
<Cursor.outputtypehandler>`. If specified on the cursor, fetch type handling is
only changed on that particular cursor. If specified on the connection, all
cursors created by that connection will have their fetch type handling changed.
The output type handler is expected to be a function with the following
signature::
handler(cursor, name, defaultType, size, precision, scale)
The parameters are the same information as the query column metadata found in
:attr:`Cursor.description`. The function is called once for each column that is
going to be fetched. The function is expected to return a
:ref:`variable object <varobj>` (generally by a call to :func:`Cursor.var()`)
or the value ``None``. The value ``None`` indicates that the default type
should be used.
Examples of output handlers are shown in :ref:`numberprecision`,
:ref:`directlobs` and :ref:`fetching-raw-data`. Also see samples such as `samples/type_handlers.py
<https://github.com/oracle/python-cx_Oracle/blob/main/samples/type_handlers.py>`__
.. _numberprecision:
Fetched Number Precision
------------------------
One reason for using an output type handler is to ensure that numeric precision
is not lost when fetching certain numbers. Oracle Database uses decimal numbers
and these cannot be converted seamlessly to binary number representations like
Python floats. In addition, the range of Oracle numbers exceeds that of
floating point numbers. Python has decimal objects which do not have these
limitations and cx_Oracle knows how to perform the conversion between Oracle
numbers and Python decimal values if directed to do so.
The following code sample demonstrates the issue:
.. code-block:: python
cur = connection.cursor()
cur.execute("create table test_float (X number(5, 3))")
cur.execute("insert into test_float values (7.1)")
connection.commit()
cur.execute("select * from test_float")
val, = cur.fetchone()
print(val, "* 3 =", val * 3)
This displays ``7.1 * 3 = 21.299999999999997``
Using Python decimal objects, however, there is no loss of precision:
.. code-block:: python
import decimal
def number_to_decimal(cursor, name, default_type, size, precision, scale):
if default_type == cx_Oracle.DB_TYPE_NUMBER:
return cursor.var(decimal.Decimal, arraysize=cursor.arraysize)
cur = connection.cursor()
cur.outputtypehandler = number_to_decimal
cur.execute("select * from test_float")
val, = cur.fetchone()
print(val, "* 3 =", val * 3)
This displays ``7.1 * 3 = 21.3``
The Python ``decimal.Decimal`` converter gets called with the string
representation of the Oracle number. The output from ``decimal.Decimal`` is
returned in the output tuple.
See `samples/return_numbers_as_decimals.py
<https://github.com/oracle/python-cx_Oracle/blob/main/samples/return_numbers_as_decimals.py>`__
.. _outconverters:
Changing Query Results with Outconverters
-----------------------------------------
cx_Oracle "outconverters" can be used with :ref:`output type handlers
<outputtypehandlers>` to change returned data.
For example, to make queries return empty strings instead of NULLs:
.. code-block:: python
def out_converter(value):
if value is None:
return ''
return value
def output_type_handler(cursor, name, default_type, size, precision, scale):
if default_type in (cx_Oracle.DB_TYPE_VARCHAR, cx_Oracle.DB_TYPE_CHAR):
return cursor.var(str, size, arraysize=cur.arraysize,
outconverter=out_converter)
connection.outputtypehandler = output_type_handler
.. _rowfactories:
Changing Query Results with Rowfactories
----------------------------------------
cx_Oracle "rowfactories" are methods called for each row that is retrieved from
the database. The :meth:`Cursor.rowfactory` method is called with the tuple that
would normally be returned from the database. The method can convert the tuple
to a different value and return it to the application in place of the tuple.
For example, to fetch each row of a query as a dictionary:
.. code-block:: python
cursor.execute("select * from locations where location_id = 1000")
columns = [col[0] for col in cursor.description]
cursor.rowfactory = lambda *args: dict(zip(columns, args))
data = cursor.fetchone()
print(data)
The output is::
{'LOCATION_ID': 1000, 'STREET_ADDRESS': '1297 Via Cola di Rie', 'POSTAL_CODE': '00989', 'CITY': 'Roma', 'STATE_PROVINCE': None, 'COUNTRY_ID': 'IT'}
If you join tables where the same column name occurs in both tables with
different meanings or values, then use a column alias in the query. Otherwise
only one of the similarly named columns will be included in the dictionary:
.. code-block:: sql
select
cat_name,
cats.color as cat_color,
dog_name,
dogs.color
from cats, dogs
.. _scrollablecursors:
Scrollable Cursors
------------------
Scrollable cursors enable applications to move backwards, forwards, to skip
rows, and to move to a particular row in a query result set. The result set is
cached on the database server until the cursor is closed. In contrast, regular
cursors are restricted to moving forward.
A scrollable cursor is created by setting the parameter ``scrollable=True``
when creating the cursor. The method :meth:`Cursor.scroll()` is used to move to
different locations in the result set.
Examples are:
.. code-block:: python
cursor = connection.cursor(scrollable=True)
cursor.execute("select * from ChildTable order by ChildId")
cursor.scroll(mode="last")
print("LAST ROW:", cursor.fetchone())
cursor.scroll(mode="first")
print("FIRST ROW:", cursor.fetchone())
cursor.scroll(8, mode="absolute")
print("ROW 8:", cursor.fetchone())
cursor.scroll(6)
print("SKIP 6 ROWS:", cursor.fetchone())
cursor.scroll(-4)
print("SKIP BACK 4 ROWS:", cursor.fetchone())
.. _fetchobjects:
Fetching Oracle Database Objects and Collections
------------------------------------------------
Oracle Database named object types and user-defined types can be fetched
directly in queries. Each item is represented as a :ref:`Python object
<objecttype>` corresponding to the Oracle Database object. This Python object
can be traversed to access its elements. Attributes including
:attr:`ObjectType.name` and :attr:`ObjectType.iscollection`, and methods
including :meth:`Object.aslist` and :meth:`Object.asdict` are available.
For example, if a table ``mygeometrytab`` contains a column ``geometry`` of
Oracle's predefined Spatial object type `SDO_GEOMETRY
<https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-683FF8C5-A773-4018-932D-2AF6EC8BC119>`__,
then it can be queried and printed:
.. code-block:: python
cur.execute("select geometry from mygeometrytab")
for obj, in cur:
dumpobject(obj)
Where ``dumpobject()`` is defined as:
.. code-block:: python
def dumpobject(obj, prefix = ""):
if obj.type.iscollection:
print(prefix, "[")
for value in obj.aslist():
if isinstance(value, cx_Oracle.Object):
dumpobject(value, prefix + " ")
else:
print(prefix + " ", repr(value))
print(prefix, "]")
else:
print(prefix, "{")
for attr in obj.type.attributes:
value = getattr(obj, attr.name)
if isinstance(value, cx_Oracle.Object):
print(prefix + " " + attr.name + ":")
dumpobject(value, prefix + " ")
else:
print(prefix + " " + attr.name + ":", repr(value))
print(prefix, "}")
This might produce output like::
{
SDO_GTYPE: 2003
SDO_SRID: None
SDO_POINT:
{
X: 1
Y: 2
Z: 3
}
SDO_ELEM_INFO:
[
1
1003
3
]
SDO_ORDINATES:
[
1
1
5
7
]
}
Other information on using Oracle objects is in :ref:`Using Bind Variables
<bind>`.
Performance-sensitive applications should consider using scalar types instead of
objects. If you do use objects, avoid calling :meth:`Connection.gettype()`
unnecessarily, and avoid objects with large numbers of attributes.
.. _rowlimit:
Limiting Rows
-------------
Query data is commonly broken into one or more sets:
- To give an upper bound on the number of rows that a query has to process,
which can help improve database scalability.
- To perform 'Web pagination' that allows moving from one set of rows to a
next, or previous, set on demand.
- For fetching of all data in consecutive small sets for batch processing.
This happens because the number of records is too large for Python to handle
at one time.
The latter can be handled by calling :meth:`Cursor.fetchmany()` with one
execution of the SQL query.
'Web pagination' and limiting the maximum number of rows are discussed in this
section. For each 'page' of results, a SQL query is executed to get the
appropriate set of rows from a table. Since the query may be executed more
than once, make sure to use :ref:`bind variables <bind>` for row numbers and
row limits.
Oracle Database 12c SQL introduced an ``OFFSET`` / ``FETCH`` clause which is
similar to the ``LIMIT`` keyword of MySQL. In Python you can fetch a set of
rows using:
.. code-block:: python
myoffset = 0 // do not skip any rows (start at row 1)
mymaxnumrows = 20 // get 20 rows
sql =
"""SELECT last_name
FROM employees
ORDER BY last_name
OFFSET :offset ROWS FETCH NEXT :maxnumrows ROWS ONLY"""
cur = connection.cursor()
for row in cur.execute(sql, offset=myoffset, maxnumrows=mymaxnumrows):
print(row)
In applications where the SQL query is not known in advance, this method
sometimes involves appending the ``OFFSET`` clause to the 'real' user query. Be
very careful to avoid SQL injection security issues.
For Oracle Database 11g and earlier there are several alternative ways
to limit the number of rows returned. The old, canonical paging query
is::
SELECT *
FROM (SELECT a.*, ROWNUM AS rnum
FROM (YOUR_QUERY_GOES_HERE -- including the order by) a
WHERE ROWNUM <= MAX_ROW)
WHERE rnum >= MIN_ROW
Here, ``MIN_ROW`` is the row number of first row and ``MAX_ROW`` is the row
number of the last row to return. For example::
SELECT *
FROM (SELECT a.*, ROWNUM AS rnum
FROM (SELECT last_name FROM employees ORDER BY last_name) a
WHERE ROWNUM <= 20)
WHERE rnum >= 1
This always has an 'extra' column, here called RNUM.
An alternative and preferred query syntax for Oracle Database 11g uses the
analytic ``ROW_NUMBER()`` function. For example to get the 1st to 20th names the
query is::
SELECT last_name FROM
(SELECT last_name,
ROW_NUMBER() OVER (ORDER BY last_name) AS myr
FROM employees)
WHERE myr BETWEEN 1 and 20
Make sure to use :ref:`bind variables <bind>` for the upper and lower limit
values.
.. _crc:
Client Result Cache
-------------------
Python cx_Oracle applications can use Oracle Database's `Client Result Cache
<https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-35CB2592-7588-4C2D-9075-6F639F25425E>`__
The CRC enables client-side caching of SQL query (SELECT statement) results in
client memory for immediate use when the same query is re-executed. This is
useful for reducing the cost of queries for small, mostly static, lookup tables,
such as for postal codes. CRC reduces network :ref:`round-trips <roundtrips>`,
and also reduces database server CPU usage.
The cache is at the application process level. Access and invalidation is
managed by the Oracle Client libraries. This removes the need for extra
application logic, or external utilities, to implement a cache.
CRC can be enabled by setting the `database parameters
<https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-A9D4A5F5-B939-48FF-80AE-0228E7314C7D>`__
``CLIENT_RESULT_CACHE_SIZE`` and ``CLIENT_RESULT_CACHE_LAG``, and then
restarting the database. For example, to set the parameters:
.. code-block:: sql
SQL> ALTER SYSTEM SET CLIENT_RESULT_CACHE_LAG = 3000 SCOPE=SPFILE;
SQL> ALTER SYSTEM SET CLIENT_RESULT_CACHE_SIZE = 64K SCOPE=SPFILE;
CRC can alternatively be configured in an :ref:`oraaccess.xml <optclientfiles>`
or :ref:`sqlnet.ora <optnetfiles>` file on the Python host, see `Client
Configuration Parameters
<https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-E63D75A1-FCAA-4A54-A3D2-B068442CE766>`__.
Tables can then be created, or altered, so repeated queries use CRC. This
allows existing applications to use CRC without needing modification. For example:
.. code-block:: sql
SQL> CREATE TABLE cities (id number, name varchar2(40)) RESULT_CACHE (MODE FORCE);
SQL> ALTER TABLE locations RESULT_CACHE (MODE FORCE);
Alternatively, hints can be used in SQL statements. For example:
.. code-block:: sql
SELECT /*+ result_cache */ postal_code FROM locations
.. _fetching-raw-data:
Fetching Raw Data
-----------------
Sometimes cx_Oracle may have problems converting data stored in the database to
Python strings. This can occur if the data stored in the database doesn't match
the character set defined by the database. The `encoding_errors` parameter to
:meth:`Cursor.var()` permits the data to be returned with some invalid data
replaced, but for additional control the parameter `bypass_decode` can be set
to `True` and cx_Oracle will bypass the decode step and return `bytes` instead
of `str` for data stored in the database as strings. The data can then be
examined and corrected as required. This approach should only be used for
troubleshooting and correcting invalid data, not for general use!
The following sample demonstrates how to use this feature:
.. code-block:: python
# define output type handler
def return_strings_as_bytes(cursor, name, default_type, size,
precision, scale):
if default_type == cx_Oracle.DB_TYPE_VARCHAR:
return cursor.var(str, arraysize=cursor.arraysize,
bypass_decode=True)
# set output type handler on cursor before fetching data
with connection.cursor() as cursor:
cursor.outputtypehandler = return_strings_as_bytes
cursor.execute("select content, charset from SomeTable")
data = cursor.fetchall()
This will produce output as::
[(b'Fianc\xc3\xa9', b'UTF-8')]
Note that last ``\xc3\xa9`` is é in UTF-8. Since this is valid UTF-8 you can then
perform a decode on the data (the part that was bypassed):
.. code-block:: python
value = data[0][0].decode("UTF-8")
This will return the value "Fiancé".
If you want to save ``b'Fianc\xc3\xa9'`` into the database directly without
using a Python string, you will need to create a variable using
:meth:`Cursor.var()` that specifies the type as
:data:`~cx_Oracle.DB_TYPE_VARCHAR` (otherwise the value will be treated as
:data:`~cx_Oracle.DB_TYPE_RAW`). The following sample demonstrates this:
.. code-block:: python
with cx_Oracle.connect(user="hr", password=userpwd,
dsn="dbhost.example.com/orclpdb1") as conn:
with conn.cursor() cursor:
var = cursor.var(cx_Oracle.DB_TYPE_VARCHAR)
var.setvalue(0, b"Fianc\xc4\x9b")
cursor.execute("""
update SomeTable set
SomeColumn = :param
where id = 1""",
param=var)
.. warning::
The database will assume that the bytes provided are in the character set
expected by the database so only use this for troubleshooting or as
directed.
.. _codecerror:
Querying Corrupt Data
---------------------
If queries fail with the error "codec can't decode byte" when you select data,
then:
* Check your :ref:`character set <globalization>` is correct. Review the
:ref:`client and database character sets <findingcharset>`. Check with
:ref:`fetching-raw-data`. Consider using UTF-8, if this is appropriate:
.. code-block:: python
connection = cx_Oracle.connect(user="hr", password=userpwd,
dsn="dbhost.example.com/orclpdb1",
encoding="UTF-8", nencoding="UTF-8")
* Check for corrupt data in the database.
If data really is corrupt, you can pass options to the internal `decode()
<https://docs.python.org/3/library/stdtypes.html#bytes.decode>`__ used by
cx_Oracle to allow it to be selected and prevent the whole query failing. Do
this by creating an :ref:`outputtypehandler <outputtypehandlers>` and setting
``encoding_errors``. For example to replace corrupt characters in character
columns:
.. code-block:: python
def output_type_handler(cursor, name, default_type, size, precision, scale):
if default_type == cx_Oracle.DB_TYPE_VARCHAR:
return cursor.var(default_type, size, arraysize=cursor.arraysize,
encoding_errors="replace")
cursor.outputtypehandler = output_type_handler
cursor.execute("select column1, column2 from SomeTableWithBadData")
Other codec behaviors can be chosen for ``encoding_errors``, see `Error Handlers
<https://docs.python.org/3/library/codecs.html#error-handlers>`__.
.. _dml:
INSERT and UPDATE Statements
============================
SQL Data Manipulation Language statements (DML) such as INSERT and UPDATE can
easily be executed with cx_Oracle. For example:
.. code-block:: python
cur = connection.cursor()
cur.execute("insert into MyTable values (:idbv, :nmbv)", [1, "Fredico"])
Do not concatenate or interpolate user data into SQL statements. See
:ref:`bind` instead.
See :ref:`txnmgmnt` for best practices on committing and rolling back data
changes.
When handling multiple data values, use :meth:`~Cursor.executemany()` for
performance. See :ref:`batchstmnt`
Inserting NULLs
---------------
Oracle requires a type, even for null values. When you pass the value None, then
cx_Oracle assumes the type is STRING. If this is not the desired type, you can
explicitly set it. For example, to insert a null :ref:`Oracle Spatial
SDO_GEOMETRY <spatial>` object:
.. code-block:: python
type_obj = connection.gettype("SDO_GEOMETRY")
cur = connection.cursor()
cur.setinputsizes(type_obj)
cur.execute("insert into sometable values (:1)", [None])

View File

@ -0,0 +1,69 @@
.. _startup:
*************************************
Starting and Stopping Oracle Database
*************************************
.. note::
**cx_Oracle has a major new release under a new name and homepage**
`python-oracledb <https://oracle.github.io/python-oracledb/>`__.
**New projects should install python-oracledb instead of cx_Oracle.**
This chapter covers how to start up and shutdown Oracle Database using
cx_Oracle.
===========================
Starting Oracle Database Up
===========================
cx_Oracle can start up a database instance. A privileged connection is
required. This example shows a script that could be run as the 'oracle'
operating system user who administers a local database installation on Linux.
It assumes that the environment variable ``ORACLE_SID`` has been set to the SID
of the database that should be started:
.. code-block:: python
# the connection must be in PRELIM_AUTH mode to perform startup
connection = cx_Oracle.connect(mode=cx_Oracle.SYSDBA | cx_Oracle.PRELIM_AUTH)
connection.startup()
# the following statements must be issued in normal SYSDBA mode
connection = cx_Oracle.connect(mode=cx_Oracle.SYSDBA, encoding="UTF-8")
cursor = connection.cursor()
cursor.execute("alter database mount")
cursor.execute("alter database open")
To start up a remote database, you may need to configure the Oracle Net
listener to use `static service registration
<https://www.oracle.com/pls/topic/lookup?ctx=dblatest&
id=GUID-0203C8FA-A4BE-44A5-9A25-3D1E578E879F>`_
by adding a ``SID_LIST_LISTENER`` entry to the database `listener.ora` file.
=============================
Shutting Oracle Database Down
=============================
cx_Oracle has the ability to shutdown the database using a privileged
connection. This example also assumes that the environment variable
``ORACLE_SID`` has been set:
.. code-block:: python
# need to connect as SYSDBA or SYSOPER
connection = cx_Oracle.connect(mode=cx_Oracle.SYSDBA)
# first shutdown() call must specify the mode, if DBSHUTDOWN_ABORT is used,
# there is no need for any of the other steps
connection.shutdown(mode=cx_Oracle.DBSHUTDOWN_IMMEDIATE)
# now close and dismount the database
cursor = connection.cursor()
cursor.execute("alter database close normal")
cursor.execute("alter database dismount")
# perform the final shutdown call
connection.shutdown(mode=cx_Oracle.DBSHUTDOWN_FINAL)

View File

@ -0,0 +1,175 @@
.. _tracingsql:
*********************************
Tracing SQL and PL/SQL Statements
*********************************
.. note::
**cx_Oracle has a major new release under a new name and homepage**
`python-oracledb <https://oracle.github.io/python-oracledb/>`__.
**New projects should install python-oracledb instead of cx_Oracle.**
Subclass Connections
====================
Subclassing enables applications to add "hooks" for connection and statement
execution. This can be used to alter, or log, connection and execution
parameters, and to extend cx_Oracle functionality.
The example below demonstrates subclassing a connection to log SQL execution
to a file. This example also shows how connection credentials can be embedded
in the custom subclass, so application code does not need to supply them.
.. code-block:: python
class Connection(cx_Oracle.Connection):
log_file_name = "log.txt"
def __init__(self):
connect_string = "hr/hr_password@dbhost.example.com/orclpdb1"
self._log("Connect to the database")
return super(Connection, self).__init__(connect_string)
def _log(self, message):
with open(self.log_file_name, "a") as f:
print(message, file=f)
def execute(self, sql, parameters):
self._log(sql)
cursor = self.cursor()
try:
return cursor.execute(sql, parameters)
except cx_Oracle.Error as e:
error_obj, = e.args
self._log(error_obj.message)
raise
connection = Connection()
connection.execute("""
select department_name
from departments
where department_id = :id""", dict(id=270))
The messages logged in ``log.txt`` are::
Connect to the database
select department_name
from departments
where department_id = :id
If an error occurs, perhaps due to a missing table, the log file would contain
instead::
Connect to the database
select department_name
from departments
where department_id = :id
ORA-00942: table or view does not exist
In production applications be careful not to log sensitive information.
See `Subclassing.py
<https://github.com/oracle/python-cx_Oracle/blob/main/
samples/subclassing.py>`__ for an example.
.. _endtoendtracing:
Oracle Database End-to-End Tracing
==================================
Oracle Database End-to-end application tracing simplifies diagnosing application
code flow and performance problems in multi-tier or multi-user environments.
The connection attributes, :attr:`~Connection.client_identifier`,
:attr:`~Connection.clientinfo`, :attr:`~Connection.dbop`,
:attr:`~Connection.module` and :attr:`~Connection.action`, set the metadata for
end-to-end tracing. You can use data dictionary and ``V$`` views to monitor
tracing or use other application tracing utilities.
The attributes are sent to the database when the next :ref:`round-trip
<roundtrips>` to the database occurs, for example when the next SQL statement is
executed.
The attribute values will remain set in connections released back to connection
pools. When the application re-acquires a connection from the pool it should
initialize the values to a desired state before using that connection.
The example below shows setting the action, module and client identifier
attributes on the connection object:
.. code-block:: python
# Set the tracing metadata
connection.client_identifier = "pythonuser"
connection.action = "Query Session tracing parameters"
connection.module = "End-to-end Demo"
for row in cursor.execute("""
SELECT username, client_identifier, module, action
FROM V$SESSION
WHERE username = 'SYSTEM'"""):
print(row)
The output will be::
('SYSTEM', 'pythonuser', 'End-to-end Demo', 'Query Session tracing parameters')
The values can also be manually set as shown by calling
`DBMS_APPLICATION_INFO procedures
<https://www.oracle.com/pls/topic/lookup?ctx=dblatest&
id=GUID-14484F86-44F2-4B34-B34E-0C873D323EAD>`__
or `DBMS_SESSION.SET_IDENTIFIER
<https://www.oracle.com/pls/topic/lookup?ctx=dblatest&
id=GUID-988EA930-BDFE-4205-A806-E54F05333562>`__. These incur round-trips to
the database, however, reducing scalability.
.. code-block:: sql
BEGIN
DBMS_SESSION.SET_IDENTIFIER('pythonuser');
DBMS_APPLICATION_INFO.set_module('End-to-End Demo');
DBMS_APPLICATION_INFO.set_action(action_name => 'Query Session tracing parameters');
END;
Low Level SQL Tracing in cx_Oracle
==================================
cx_Oracle is implemented using the `ODPI-C <https://oracle.github.io/odpi>`__
wrapper on top of the Oracle Client libraries. The ODPI-C tracing capability
can be used to log executed cx_Oracle statements to the standard error stream.
Before executing Python, set the environment variable ``DPI_DEBUG_LEVEL`` to
16.
At a Windows command prompt, this could be done with::
set DPI_DEBUG_LEVEL=16
On Linux, you might use::
export DPI_DEBUG_LEVEL=16
After setting the variable, run the Python Script, for example on Linux::
python end-to-endtracing.py 2> log.txt
For an application that does a single query, the log file might contain a
tracing line consisting of the prefix 'ODPI', a thread identifier, a timestamp,
and the SQL statement executed::
ODPI [26188] 2019-03-26 09:09:03.909: ODPI-C 3.1.1
ODPI [26188] 2019-03-26 09:09:03.909: debugging messages initialized at level 16
ODPI [26188] 2019-03-26 09:09:09.917: SQL SELECT * FROM jobss
Traceback (most recent call last):
File "end-to-endtracing.py", line 14, in <module>
cursor.execute("select * from jobss")
cx_Oracle.DatabaseError: ORA-00942: table or view does not exist
See `ODPI-C Debugging
<https://oracle.github.io/odpi/doc/user_guide/debugging.html>`__ for
documentation on ``DPI_DEBUG_LEVEL``.

View File

@ -0,0 +1,406 @@
.. _tuning:
****************
Tuning cx_Oracle
****************
.. note::
**cx_Oracle has a major new release under a new name and homepage**
`python-oracledb <https://oracle.github.io/python-oracledb/>`__.
**New projects should install python-oracledb instead of cx_Oracle.**
Some general tuning tips are:
* Tune your application architecture.
A general application goal is to reduce the number of :ref:`round-trips
<roundtrips>` between cx_Oracle and the database.
For multi-user applications, make use of connection pooling. Create the pool
once during application initialization. Do not oversize the pool, see
:ref:`connpool` . Use a session callback function to set session state, see
:ref:`Session CallBacks for Setting Pooled Connection State <sessioncallback>`.
Make use of efficient cx_Oracle functions. For example, to insert
multiple rows use :meth:`Cursor.executemany()` instead of
:meth:`Cursor.execute()`.
* Tune your SQL statements. See the `SQL Tuning Guide
<https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=TGSQL>`__.
Use :ref:`bind variables <bind>` to avoid statement reparsing.
Tune :attr:`Cursor.arraysize` and :attr:`Cursor.prefetchrows` for each query,
see :ref:`Tuning Fetch Performance <tuningfetch>`.
Do simple optimizations like :ref:`limiting the number of rows <rowlimit>` and
avoiding selecting columns not used in the application.
It may be faster to work with simple scalar relational values than to use
Oracle Database object types.
Make good use of PL/SQL to avoid executing many individual statements from
cx_Oracle.
Tune the :ref:`Statement Cache <stmtcache>`.
Enable :ref:`Client Result Caching <clientresultcache>` for small lookup tables.
* Tune your database. See the `Database Performance Tuning Guide
<https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=TGDBA>`__.
* Tune your network. For example, when inserting or retrieving a large number
of rows (or for large data), or when using a slow network, then tune the
Oracle Network Session Data Unit (SDU) and socket buffer sizes, see `Oracle
Net Services: Best Practices for Database Performance and High Availability
<https://static.rainfocus.com/oracle/oow19/sess/1553616880266001WLIh/PF/OOW19_Net_CON4641_1569022126580001esUl.pdf>`__.
* Do not commit or rollback unnecessarily. Use :attr:`Connection.autocommit` on
the last of a sequence of DML statements.
.. _tuningfetch:
Tuning Fetch Performance
========================
To tune queries you can adjust cx_Oracle's internal buffer sizes to improve the
speed of fetching rows across the network from the database, and to optimize
memory usage. Regardless of which cx_Oracle method is used to get query
results, internally all rows are fetched in batches from the database and
buffered before being returned to the application. The internal buffer sizes
can have a significant performance impact. The sizes do not affect how, or
when, rows are returned to your application. They do not affect the minimum or
maximum number of rows returned by a query.
For best performance, tune "array fetching" with :attr:`Cursor.arraysize` and
"row prefetching" with :attr:`Cursor.prefetchrows` before calling
:meth:`Cursor.execute()`. Queries that return LOBs and similar types will never
prefetch rows, so the ``prefetchrows`` value is ignored in those cases.
The common query tuning scenario is for SELECT statements that return a large
number of rows over a slow network. Increasing ``arraysize`` can improve
performance by reducing the number of :ref:`round-trips <roundtrips>` to the
database. However increasing this value increases the amount of memory
required. Adjusting ``prefetchrows`` will also affect performance and memory
usage.
Row prefetching and array fetching are both internal buffering techniques to
reduce :ref:`round-trips <roundtrips>` to the database. The difference is the
code layer that is doing the buffering, and when the buffering occurs. The
Oracle Client libraries used by cx_Oracle have separate "execute SQL statement"
and "fetch data" calls. Prefetching allows query results to be returned to the
application when the successful statement execution acknowledgment is returned
from the database. This means that a subsequent internal "fetch data" operation
does not always need to make a round-trip to the database because rows are
already buffered in the Oracle Client libraries. Reducing round-trips helps
performance and scalability. An overhead of prefetching is the need for an
additional data copy from Oracle Client's prefetch buffers.
Choosing values for ``arraysize`` and ``prefetchrows``
++++++++++++++++++++++++++++++++++++++++++++++++++++++
The best :attr:`Cursor.arraysize` and :attr:`Cursor.prefetchrows` values can be
found by experimenting with your application under the expected load of normal
application use. This is because the cost of the extra memory copy from the
prefetch buffers when fetching a large quantity of rows or very "wide" rows may
outweigh the cost of a round-trip for a single cx_Oracle user on a fast network.
However under production application load, the reduction of round-trips may help
performance and overall system scalability. The documentation in
:ref:`round-trips <roundtrips>` shows how to measure round-trips.
Here are some suggestions for the starting point to begin your tuning:
* To tune queries that return an unknown number of rows, estimate the number of
rows returned and start with an appropriate :attr:`Cursor.arraysize` value.
The default is 100. Then set :attr:`Cursor.prefetchrows` to the ``arraysize``
value. For example:
.. code-block:: python
cur = connection.cursor()
cur.prefetchrows = 1000
cur.arraysize = 1000
for row in cur.execute("SELECT * FROM very_big_table"):
print(row)
Adjust the values as needed for performance, memory and round-trip usage. Do
not make the sizes unnecessarily large. For a large quantity of rows or very
"wide" rows on fast networks you may prefer to leave ``prefetchrows`` at its
default value of 2. Keep ``arraysize`` as big, or bigger than,
``prefetchrows``.
* If you are fetching a fixed number of rows, start your tuning by setting
``arraysize`` to the number of expected rows, and set ``prefetchrows`` to one
greater than this value. (Adding one removes the need for a round-trip to check
for end-of-fetch). For example, if you are querying 20 rows, perhaps to
:ref:`display a page <rowlimit>` of data, set ``prefetchrows`` to 21 and
``arraysize`` to 20:
.. code-block:: python
cur = connection.cursor()
cur.prefetchrows = 21
cur.arraysize = 20
for row in cur.execute("""
SELECT last_name
FROM employees
ORDER BY last_name
OFFSET 0 ROWS FETCH NEXT 20 ROWS ONLY"""):
print(row)
This will return all rows for the query in one round-trip.
* If you know that a query returns just one row then set :attr:`Cursor.arraysize`
to 1 to minimize memory usage. The default prefetch value of 2 allows minimal
round-trips for single-row queries:
.. code-block:: python
cur = connection.cursor()
cur.arraysize = 1
cur.execute("select * from MyTable where id = 1"):
row = cur.fetchone()
print(row)
In cx_Oracle, the ``arraysize`` and ``prefetchrows`` values are only examined
when a statement is executed the first time. To change the values, create a new
cursor. For example, to change ``arraysize`` for a repeated statement:
.. code-block:: python
array_sizes = (10, 100, 1000)
for size in array_sizes:
cursor = connection.cursor()
cursor.arraysize = size
start = time.time()
cursor.execute(sql).fetchall()
elapsed = time.time() - start
print("Time for", size, elapsed, "seconds")
There are two cases that will benefit from setting :attr:`Cursor.prefetchrows`
to 0:
* When passing REF CURSORS into PL/SQL packages. Setting ``prefetchrows`` to 0
can stop rows being prematurely (and silently) fetched into cx_Oracle's
internal buffers, making them unavailable to the PL/SQL code that receives the
REF CURSOR.
* When querying a PL/SQL function that uses PIPE ROW to emit rows at
intermittent intervals. By default, several rows needs to be emitted by the
function before cx_Oracle can return them to the application. Setting
``prefetchrows`` to 0 helps give a consistent flow of data to the application.
Prefetching can also be enabled in an external :ref:`oraaccess.xml
<optclientfiles>` file, which may be useful for tuning an application when
modifying its code is not feasible. Setting the size in ``oraaccess.xml`` will
affect the whole application, so it should not be the first tuning choice.
One place where increasing ``arraysize`` is particularly useful is in copying
data from one database to another:
.. code-block:: python
# setup cursors
source_cursor = source_connection.cursor()
source_cursor.arraysize = 1000
target_cursor = target_connection.cursor()
# perform fetch and bulk insertion
source_cursor.execute("select * from MyTable")
while True:
rows = source_cursor.fetchmany()
if not rows:
break
target_cursor.executemany("insert into MyTable values (:1, :2)", rows)
target_connection.commit()
Tuning REF CURSORS
++++++++++++++++++
In cx_Oracle, REF CURSORS can also be tuned by setting the values of ``arraysize``
and ``prefetchrows``. The prefetchrows value must be set before calling the PL/SQL
procedure as the REF CURSOR is executed on the server.
For example:
.. code-block:: python
# Set the arraysize and prefetch rows of the REF cursor
ref_cursor = connection.cursor()
ref_cursor.prefetchrows = 1000
ref_cursor.arraysize = 1000
# Perform the tuned fetch
sum_rows = 0
cursor.callproc("myrefcursorproc", [ref_cursor])
print("Sum of IntCol for", num_rows, "rows:")
for row in ref_cursor:
sum_rows += row[0]
print(sum_rows)
.. _roundtrips:
Database Round-trips
====================
A round-trip is defined as the trip from the Oracle Client libraries (used by
cx_Oracle) to the database and back. Calling each cx_Oracle function, or
accessing each attribute, will require zero or more round-trips. Along with
tuning an application's architecture and `tuning its SQL statements
<https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=TGSQL>`__, a general
performance and scalability goal is to minimize `round-trips
<https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-9B2F05F9-D841-4493-A42D-A7D89694A2D1>`__.
Some general tips for reducing round-trips are:
* Tune :attr:`Cursor.arraysize` and :attr:`Cursor.prefetchrows` for each query.
* Use :meth:`Cursor.executemany()` for optimal DML execution.
* Only commit when necessary. Use :attr:`Connection.autocommit` on the last statement of a transaction.
* For connection pools, use a callback to set connection state, see :ref:`Session CallBacks for Setting Pooled Connection State <sessioncallback>`.
* Make use of PL/SQL procedures which execute multiple SQL statements instead of executing them individually from cx_Oracle.
* Use scalar types instead of Oracle Database object types.
* Avoid overuse of :meth:`Connection.ping()`.
* Avoid setting :data:`SessionPool.ping_interval` to 0 or a small value.
* When using SODA, use pooled connections and enable the :ref:`SODA metadata cache <sodametadatacache>`.
Finding the Number of Round-Trips
+++++++++++++++++++++++++++++++++
Oracle's `Automatic Workload Repository
<https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-56AEF38E-9400-427B-A818-EDEC145F7ACD>`__
(AWR) reports show 'SQL*Net roundtrips to/from client' and are useful for
finding the overall behavior of a system.
Sometimes you may wish to find the number of round-trips used for a
specific application. Snapshots of the ``V$SESSTAT`` view taken before
and after doing some work can be used for this:
.. code-block:: sql
SELECT ss.value, sn.display_name
FROM v$sesstat ss, v$statname sn
WHERE ss.sid = SYS_CONTEXT('USERENV','SID')
AND ss.statistic# = sn.statistic#
AND sn.name LIKE '%roundtrip%client%';
.. _stmtcache:
Statement Caching
=================
cx_Oracle's :meth:`Cursor.execute()` and :meth:`Cursor.executemany()` functions
use the `Oracle Call Interface statement cache
<https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-4947CAE8-1F00-4897-BB2B-7F921E495175>`__
for efficient re-execution of statements. Statement caching lets Oracle
Database cursors be used without re-parsing the statement. Statement caching
also reduces metadata transfer costs between cx_Oracle and the database.
Performance and scalability are improved.
Each standalone or pooled connection has its own cache of statements with a
default size of 20. The size can be set when creating connection pools or
standalone connections. The size can subsequently be changed with
:attr:`Connection.stmtcachesize` or :attr:`SessionPool.stmtcachesize`. In
general, set the statement cache size to the size of the working set of
statements being executed by the application. To manually tune the cache,
monitor the general application load and the `Automatic Workload Repository
<https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-56AEF38E-9400-427B-A818-EDEC145F7ACD>`__
(AWR) "bytes sent via SQL*Net to client" values. The latter statistic should
benefit from not shipping statement metadata to cx_Oracle. Adjust the statement
cache size to your satisfaction. With Oracle Database 12c, or later, the
statement cache size can be automatically tuned using an :ref:`oraaccess.xml
<optclientfiles>` file.
Statement caching can be disabled by setting the size to 0. Disabling
the cache may be beneficial when the quantity or order of statements
causes cache entries to be flushed before they get a chance to be
reused. For example if there are more distinct statements than cache
slots, and the order of statement execution causes older statements to
be flushed from the cache before the statements are re-executed.
With connection pools, the effect of changing :attr:`SessionPool.stmtcachesize`
after pool creation depends on the Oracle Client version:
- When using Oracle Client 21 (or later), changing the cache size does not
immediately affect connections previously acquired and currently in use. When
those connections are subsequently released to the pool and re-acquired, they
will then use the new value. If it is neccessary to change the size on a
connection because it is not being released to the pool, use
:data:`Connection.stmtcachesize`.
- When using Oracle Client prior to version 21, changing the pool's statement
cache size has no effect on connections that already exist in the pool but
will affect new connections that are subsequently created, for example when
the pool grows. To change the size on a connection, use
:data:`Connection.stmtcachesize`.
When it is inconvenient to pass statement text through an application, the
:meth:`Cursor.prepare()` call can be used to avoid statement re-parsing.
Subsequent ``execute()`` calls use the value ``None`` instead of the SQL text:
.. code-block:: python
cur.prepare("select * from dept where deptno = :id order by deptno")
cur.execute(None, id = 20)
res = cur.fetchall()
print(res)
cur.execute(None, id = 10)
res = cur.fetchall()
print(res)
Statements passed to :meth:`~Cursor.prepare()` are also stored in the statement
cache.
.. _clientresultcache:
Client Result Caching
=====================
cx_Oracle applications can use Oracle Database's `Client Result Cache
<https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-35CB2592-7588-4C2D-9075-6F639F25425E>`__.
The CRC enables client-side caching of SQL query (SELECT statement) results in
client memory for immediate use when the same query is re-executed. This is
useful for reducing the cost of queries for small, mostly static, lookup tables,
such as for postal codes. CRC reduces network :ref:`round-trips <roundtrips>`,
and also reduces database server CPU usage.
The cache is at the application process level. Access and invalidation is
managed by the Oracle Client libraries. This removes the need for extra
application logic, or external utilities, to implement a cache.
CRC can be enabled by setting the `database parameters
<https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-A9D4A5F5-B939-48FF-80AE-0228E7314C7D>`__
``CLIENT_RESULT_CACHE_SIZE`` and ``CLIENT_RESULT_CACHE_LAG``, and then
restarting the database, for example:
.. code-block:: sql
SQL> ALTER SYSTEM SET CLIENT_RESULT_CACHE_LAG = 3000 SCOPE=SPFILE;
SQL> ALTER SYSTEM SET CLIENT_RESULT_CACHE_SIZE = 64K SCOPE=SPFILE;
SQL> STARTUP FORCE
CRC can alternatively be configured in an :ref:`oraaccess.xml <optclientfiles>`
or :ref:`sqlnet.ora <optnetfiles>` file on the Python host, see `Client
Configuration Parameters
<https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-E63D75A1-FCAA-4A54-A3D2-B068442CE766>`__.
Tables can then be created, or altered, so repeated queries use CRC. This
allows existing applications to use CRC without needing modification. For example:
.. code-block:: sql
SQL> CREATE TABLE cities (id number, name varchar2(40)) RESULT_CACHE (MODE FORCE);
SQL> ALTER TABLE locations RESULT_CACHE (MODE FORCE);
Alternatively, hints can be used in SQL statements. For example:
.. code-block:: sql
SELECT /*+ result_cache */ postal_code FROM locations

View File

@ -0,0 +1,84 @@
.. _txnmgmnt:
**********************
Transaction Management
**********************
.. note::
**cx_Oracle has a major new release under a new name and homepage**
`python-oracledb <https://oracle.github.io/python-oracledb/>`__.
**New projects should install python-oracledb instead of cx_Oracle.**
A database transaction is a grouping of SQL statements that make a logical data
change to the database.
When :meth:`Cursor.execute()` executes a SQL statement, a transaction is
started or continued. By default, cx_Oracle does not commit this transaction
to the database. The methods :meth:`Connection.commit()` and
:meth:`Connection.rollback()` methods can be used to explicitly commit
or rollback a transaction:
.. code-block:: python
cursor.execute("INSERT INTO mytab (name) VALUES ('John')")
connection.commit()
When a database connection is closed, such as with :meth:`Connection.close()`,
or when variables referencing the connection go out of scope, any uncommitted
transaction will be rolled back.
Autocommitting
==============
An alternative way to commit is to set the attribute
:attr:`~Connection.autocommit` of the connection to ``True``. This ensures all
:ref:`DML <dml>` statements (INSERT, UPDATE etc) are committed as they are
executed. Unlike :meth:`Connection.commit()`, this does not require an
additional :ref:`round-trip <roundtrips>` to the database so it is more
efficient when used appropriately.
Note that irrespective of the autocommit value, Oracle Database will always
commit an open transaction when a DDL statement is executed.
When executing multiple DML statements that constitute a single transaction, it
is recommended to use autocommit mode only for the last DML statement in the
sequence of operations. Unnecessarily committing causes extra database load,
and can destroy transactional consistency.
The example below shows a new customer being added to the table ``CUST_TABLE``.
The corresponding ``SALES`` table is updated with a purchase of 3000 pens from
the customer. The final insert uses autocommit mode to commit both new
records:
.. code-block:: python
# Add a new customer
id_var = cursor.var(int)
connection.autocommit = False # make sure any previous value is off
cursor.execute("""
INSERT INTO cust_table (name) VALUES ('John')
RETURNING id INTO :bvid""", bvid=id_var)
# Add sales data for the new customer and commit all new values
id_val = id_var.getvalue()[0]
connection.autocommit = True
cursor.execute("INSERT INTO sales_table VALUES (:bvid, 'pens', 3000)",
bvid=id_val)
Explicit Transactions
=====================
The method :meth:`Connection.begin()` can be used to explicitly start a local
or global transaction.
Without parameters, this explicitly begins a local transaction; otherwise, this
explicitly begins a distributed (global) transaction with the given parameters.
See the Oracle documentation for more details.
Note that in order to make use of global (distributed) transactions, the
attributes :attr:`Connection.internal_name` and
:attr:`Connection.external_name` attributes must be set.

View File

@ -0,0 +1,75 @@
.. _xmldatatype:
********************
Working with XMLTYPE
********************
.. note::
**cx_Oracle has a major new release under a new name and homepage**
`python-oracledb <https://oracle.github.io/python-oracledb/>`__.
**New projects should install python-oracledb instead of cx_Oracle.**
Oracle XMLType columns are fetched as strings by default. This is currently
limited to the maximum length of a ``VARCHAR2`` column. To return longer XML
values, they must be queried as LOB values instead.
The examples below demonstrate using XMLType data with cx_Oracle. The
following table will be used in these examples:
.. code-block:: sql
CREATE TABLE xml_table (
id NUMBER,
xml_data SYS.XMLTYPE
);
Inserting into the table can be done by simply binding a string as shown:
.. code-block:: python
xml_data = """<?xml version="1.0"?>
<customer>
<name>John Smith</name>
<Age>43</Age>
<Designation>Professor</Designation>
<Subject>Mathematics</Subject>
</customer>"""
cursor.execute("insert into xml_table values (:id, :xml)",
id=1, xml=xml_data)
This approach works with XML strings up to 1 GB in size. For longer strings, a
temporary CLOB must be created using :meth:`Connection.createlob()` and bound
as shown:
.. code-block:: python
clob = connection.createlob(cx_Oracle.DB_TYPE_CLOB)
clob.write(xml_data)
cursor.execute("insert into xml_table values (:id, sys.xmltype(:xml))",
id=2, xml=clob)
Fetching XML data can be done simply for values that are shorter than the
length of a VARCHAR2 column, as shown:
.. code-block:: python
cursor.execute("select xml_data from xml_table where id = :id", id=1)
xml_data, = cursor.fetchone()
print(xml_data) # will print the string that was originally stored
For values that exceed the length of a VARCHAR2 column, a CLOB must be returned
instead by using the function ``XMLTYPE.GETCLOBVAL()`` as shown:
.. code-block:: python
cursor.execute("""
select xmltype.getclobval(xml_data)
from xml_table
where id = :id""", id=1)
clob, = cursor.fetchone()
print(clob.read())
The LOB that is returned can be streamed or a string can be returned instead of
a CLOB. See :ref:`lobdata` for more information about processing LOBs.

View File

@ -1,171 +0,0 @@
.. _whatsnew:
**********
What's New
**********
.. _whatsnew60:
cx_Oracle 6.0
=============
This document contains a summary of the changes in cx_Oracle 6 compared to
cx_Oracle 5.3. cx_Oracle 6.0 was released on August 14, 2017. See the
:ref:`release notes <releasenotes60>` for complete details.
Highlights
----------
- Has been re-implemented to use the new
`ODPI-C <https://oracle.github.io/odpi>`__ abstraction layer for Oracle
Database. The cx_Oracle API is unchanged. The cx_Oracle design, build and
linking process has improved because of ODPI-C.
- Now has Python Wheels available for install. This is made possible by the
ODPI-C architecture. Windows installers and Linux RPMs are no longer
produced since PyPI no longer supports them.
- Has less code in Python's Global Interpreter Lock, giving better
scalability.
- Added support for universal rowids.
- Added support for DML returning of multiple rows.
- Now associates LOB locators to LOB objects so they are not overwritten on
database round trips.
Installation Changes
--------------------
- On Linux, cx_Oracle 6 no longer uses instant client RPMs automatically.
You must set LD_LIBRARY_PATH or use ldconfig to locate the Oracle Client
library.
- On platforms other than Windows, if ORACLE_HOME is set (in a database or
full client installation), remove requirement to set LD_LIBRARY_PATH in
order to locate the Oracle Client library
(`issue 20 <https://github.com/oracle/odpi/issues/20>`__).
Connection Management Enhancements
----------------------------------
- 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.
- 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 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).
- Removed requirement that encoding and nencoding both be specified when
creating a connection or session pool. The missing value is set to its
default value if one of the values is set and the other is not
(`issue 36 <https://github.com/oracle/python-cx_Oracle/issues/36>`__).
- Permit use of both string and unicode for Python 2.7 for creating session
pools and for changing passwords
(`issue 23 <https://github.com/oracle/python-cx_Oracle/issues/23>`__).
Data Type and Data Handling Enhancements
----------------------------------------
- Added attributes :attr:`Variable.actualElements` and
:attr:`Variable.values` to variables.
- Added support for smallint and float data types in Oracle objects, as
requested
(`issue 4 <https://github.com/oracle/python-cx_Oracle/issues/4>`__).
- 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.
- 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()`.
- 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.
The method :meth:`Cursor.setoutputsize` no longer does anything, since
ODPI-C automatically manages buffer sizes of LONG and LONG RAW columns.
- Enable temporary LOB caching in order to avoid disk I/O as suggested
(`issue 10 <https://github.com/oracle/odpi/issues/10>`__).
Error Handling Enhancements
---------------------------
- Provide improved error message when OCI environment cannot be created,
such as when the oraaccess.xml file cannot be processed properly.
- 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.
Test Enhancements
-----------------
- 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.
Removals
--------
- 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 <https://github.com/oracle/
python-cx_Oracle/blob/master/samples/AppContext.py>`__.
- Dropped deprecated attribute numbersAsString from
:ref:`cursor objects <cursorobj>`. Use an output type handler instead as
shown in this `sample <https://github.com/oracle/python-cx_Oracle/blob/
master/samples/ReturnNumbersAsDecimals.py>`__.
- Dropped deprecated attributes cqqos and rowids from
:ref:`subscription objects <subscrobj>`. Use the qos attribute instead as
shown in this `sample <https://github.com/oracle/python-cx_Oracle/blob/
master/samples/CQN.py>`__.
- Dropped deprecated parameters cqqos and rowids from the
:meth:`Connection.subscribe()` method. Use the qos parameter instead as
shown in this `sample <https://github.com/oracle/python-cx_Oracle/blob/
master/samples/CQN.py>`__.

2
odpi

@ -1 +1 @@
Subproject commit 34da7033238754efc64ffbc7f006b5ba47fc6e9b
Subproject commit f73a7c13d643b3fe252614bafc930afbd8e287dd

3
pyproject.toml Normal file
View File

@ -0,0 +1,3 @@
[build-system]
requires = ["setuptools >= 40.6.0", "wheel"]
build-backend = "setuptools.build_meta"

View File

@ -1,36 +0,0 @@
#------------------------------------------------------------------------------
# Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
#------------------------------------------------------------------------------
#------------------------------------------------------------------------------
# BindInsert.py
#
# Demonstrate how to insert a row into a table using bind variables.
#------------------------------------------------------------------------------
from __future__ import print_function
import cx_Oracle
import SampleEnv
connection = cx_Oracle.connect(SampleEnv.GetMainConnectString())
rows = [ (1, "First" ),
(2, "Second" ),
(3, "Third" ),
(4, "Fourth" ),
(5, "Fifth" ),
(6, "Sixth" ),
(7, "Seventh" ) ]
cursor = connection.cursor()
cursor.executemany("insert into mytab(id, data) values (:1, :2)", rows)
# Don't commit - this lets us run the demo multiple times
#connection.commit()
# Now query the results back
for row in cursor.execute('select * from mytab'):
print(row)

View File

@ -1,28 +0,0 @@
#------------------------------------------------------------------------------
# Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
#------------------------------------------------------------------------------
#------------------------------------------------------------------------------
# DropSamples.py
#
# Drops the database objects used for the cx_Oracle samples.
#------------------------------------------------------------------------------
from __future__ import print_function
import cx_Oracle
import SampleEnv
def DropSamples(conn):
print("Dropping sample schemas and edition...")
SampleEnv.RunSqlScript(conn, "DropSamples",
main_user = SampleEnv.GetMainUser(),
edition_user = SampleEnv.GetEditionUser(),
edition_name = SampleEnv.GetEditionName())
if __name__ == "__main__":
conn = cx_Oracle.connect(SampleEnv.GetSysdbaConnectString(),
mode = cx_Oracle.SYSDBA)
DropSamples(conn)
print("Done.")

View File

@ -1,41 +1,59 @@
This directory contains samples for cx_Oracle.
# Samples
## News
**cx_Oracle has a major new release under a new name and homepage
[python-oracledb](https://oracle.github.io/python-oracledb/).**
**New projects should install python-oracledb instead of cx_Oracle: python -m pip install oracledb**
**The new source code and samples can be found at
[github.com/oracle/python-oracledb](https://github.com/oracle/python-oracledb).**
## cx_Oracle Examples
This directory contains samples for [cx_Oracle][6]. Documentation is
[here][7]. A separate tutorial is [here][8].
1. The schemas and SQL objects that are referenced in the samples can be
created by running the Python script [SetupSamples.py][1]. The script
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
[SampleEnv.py][2]. Run the script using the following command:
[sample_env.py][2]. Run the script using the following command:
python SetupSamples.py
python setup_samples.py
Alternatively, the [SQL script][3] can be run directly via SQL\*Plus, which
will always prompt for the names of the schemas and edition that will be
created.
sqlplus sys/syspassword@hostname/servicename @sql/SetupSamples.sql
sqlplus sys/syspassword@hostname/servicename @sql/setup_samples.sql
2. Run a Python script, for example:
python Query.py
python query.py
3. After running cx_Oracle samples, the schemas and SQL objects can be
dropped by running the Python script [DropSamples.py][4]. The script
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
[SampleEnv.py][2]. Run the script using the following command:
[sample_env.py][2]. Run the script using the following command:
python DropSamples.py
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/DropSamples.sql
sqlplus sys/syspassword@hostname/servicename @sql/drop_samples.sql
[1]: https://github.com/oracle/python-cx_Oracle/blob/master/samples/SetupSamples.py
[2]: https://github.com/oracle/python-cx_Oracle/blob/master/samples/SampleEnv.py
[3]: https://github.com/oracle/python-cx_Oracle/blob/master/samples/sql/SetupSamples.sql
[4]: https://github.com/oracle/python-cx_Oracle/blob/master/samples/DropSamples.py
[5]: https://github.com/oracle/python-cx_Oracle/blob/master/samples/sql/DropSamples.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

View File

@ -1,31 +0,0 @@
#------------------------------------------------------------------------------
# Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved.
#------------------------------------------------------------------------------
#------------------------------------------------------------------------------
# RefCursor.py
# Demonstrates the use of REF cursors with cx_Oracle.
#------------------------------------------------------------------------------
from __future__ import print_function
import cx_Oracle
import SampleEnv
connection = cx_Oracle.connect(SampleEnv.GetMainConnectString())
cursor = connection.cursor()
refCursor = connection.cursor()
cursor.callproc("myrefcursorproc", (2, 6, refCursor))
print("Rows between 2 and 6:")
for row in refCursor:
print(row)
print()
refCursor = connection.cursor()
cursor.callproc("myrefcursorproc", (8, 9, refCursor))
print("Rows between 8 and 9:")
for row in refCursor:
print(row)
print()

View File

@ -1,33 +0,0 @@
#------------------------------------------------------------------------------
# Copyright (c) 2016, 2019, 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.
#------------------------------------------------------------------------------
#------------------------------------------------------------------------------
# ReturnUnicode.py
# Returns all strings as unicode. This also demonstrates the use of an output
# type handler to change the way in which data is returned from a cursor.
#
# This script requires cx_Oracle 5.0 and higher and will only work in Python 2.
#------------------------------------------------------------------------------
from __future__ import print_function
import cx_Oracle
import SampleEnv
def OutputTypeHandler(cursor, name, defaultType, size, precision, scale):
if defaultType in (cx_Oracle.STRING, cx_Oracle.FIXED_CHAR):
return cursor.var(unicode, size, cursor.arraysize)
connection = cx_Oracle.connect(SampleEnv.GetMainConnectString())
connection.outputtypehandler = OutputTypeHandler
cursor = connection.cursor()
cursor.execute("select * from TestStrings")
for row in cursor:
print("Row:", row)

View File

@ -1,148 +0,0 @@
#------------------------------------------------------------------------------
# Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved.
#------------------------------------------------------------------------------
#------------------------------------------------------------------------------
# Sets the environment used by most Python cx_Oracle samples. 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_SYSDBA_USER: SYSDBA user for setting up samples
# CX_ORACLE_SAMPLES_SYSDBA_PASSWORD: SYSDBA 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 cx_Oracle is 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/orclpdb" or "localhost/XE"
#
# 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.
#------------------------------------------------------------------------------
from __future__ import print_function
import getpass
import os
import sys
# for Python 2.7 we need raw_input
try:
input = raw_input
except NameError:
pass
# default values
DEFAULT_MAIN_USER = "pythondemo"
DEFAULT_EDITION_USER = "pythoneditions"
DEFAULT_EDITION_NAME = "python_e1"
DEFAULT_CONNECT_STRING = "localhost/orclpdb"
# 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 GetValue(name, label, defaultValue=""):
value = PARAMETERS.get(name)
if value is not None:
return value
envName = "CX_ORACLE_SAMPLES_" + name
value = os.environ.get(envName)
if value is None:
if defaultValue:
label += " [%s]" % defaultValue
label += ": "
if defaultValue:
value = input(label).strip()
else:
value = getpass.getpass(label)
if not value:
value = defaultValue
PARAMETERS[name] = value
return value
def GetMainUser():
return GetValue("MAIN_USER", "Main User Name", DEFAULT_MAIN_USER)
def GetMainPassword():
return GetValue("MAIN_PASSWORD", "Password for %s" % GetMainUser())
def GetEditionUser():
return GetValue("EDITION_USER", "Edition User Name", DEFAULT_EDITION_USER)
def GetEditionPassword():
return GetValue("EDITION_PASSWORD", "Password for %s" % GetEditionUser())
def GetEditionName():
return GetValue("EDITION_NAME", "Edition Name", DEFAULT_EDITION_NAME)
def GetConnectString():
return GetValue("CONNECT_STRING", "Connect String", DEFAULT_CONNECT_STRING)
def GetMainConnectString(password=None):
if password is None:
password = GetMainPassword()
return "%s/%s@%s" % (GetMainUser(), password, GetConnectString())
def GetDrcpConnectString():
return GetMainConnectString() + ":pooled"
def GetEditionConnectString():
return "%s/%s@%s" % \
(GetEditionUser(), GetEditionPassword(), GetConnectString())
def GetSysdbaConnectString():
sysdbaUser = GetValue("SYSDBA_USER", "SYSDBA user", "sys")
sysdbaPassword = GetValue("SYSDBA_PASSWORD",
"Password for %s" % sysdbaUser)
return "%s/%s@%s" % (sysdbaUser, sysdbaPassword, GetConnectString())
def RunSqlScript(conn, scriptName, **kwargs):
statementParts = []
cursor = conn.cursor()
replaceValues = [("&" + k + ".", v) for k, v in kwargs.items()] + \
[("&" + k, v) for k, v in kwargs.items()]
scriptDir = os.path.dirname(os.path.abspath(sys.argv[0]))
fileName = os.path.join(scriptDir, "sql", scriptName + "Exec.sql")
for line in open(fileName):
if line.strip() == "/":
statement = "".join(statementParts).strip()
if statement:
for searchValue, replaceValue in replaceValues:
statement = statement.replace(searchValue, replaceValue)
cursor.execute(statement)
statementParts = []
else:
statementParts.append(line)
cursor.execute("""
select name, type, line, position, text
from dba_errors
where owner = upper(:owner)
order by name, type, line, position""",
owner = GetMainUser())
prevName = prevObjType = None
for name, objType, lineNum, position, text in cursor:
if name != prevName or objType != prevObjType:
print("%s (%s)" % (name, objType))
prevName = name
prevObjType = objType
print(" %s/%s %s" % (lineNum, position, text))

View File

@ -1,105 +0,0 @@
#------------------------------------------------------------------------------
# Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
#------------------------------------------------------------------------------
#------------------------------------------------------------------------------
# SessionCallbackPLSQL.py
#
# Demonstrate how to use a 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
# SessionCallback.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 and higher.
#------------------------------------------------------------------------------
from __future__ import print_function
import cx_Oracle
import SampleEnv
# create pool with session callback defined
pool = cx_Oracle.SessionPool(SampleEnv.GetMainUser(),
SampleEnv.GetMainPassword(), SampleEnv.GetConnectString(), min=2,
max=5, increment=1, threaded=True,
sessionCallback="pkg_SessionCallback.TheCallback")
# truncate table logging calls to PL/SQL session callback
conn = pool.acquire()
cursor = conn.cursor()
cursor.execute("truncate table PLSQLSessionCallbacks")
conn.close()
# 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")
conn = pool.acquire()
cursor = conn.cursor()
cursor.execute("select to_char(current_date) from dual")
result, = cursor.fetchone()
print("main(): result is", repr(result))
conn.close()
# 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")
conn = pool.acquire(tag="NLS_DATE_FORMAT=SIMPLE")
cursor = conn.cursor()
cursor.execute("select to_char(current_date) from dual")
result, = cursor.fetchone()
print("main(): result is", repr(result))
conn.close()
# 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")
conn = pool.acquire(tag="NLS_DATE_FORMAT=SIMPLE")
cursor = conn.cursor()
cursor.execute("select to_char(current_date) from dual")
result, = cursor.fetchone()
print("main(): result is", repr(result))
conn.close()
# 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")
conn = pool.acquire(tag="NLS_DATE_FORMAT=FULL;TIME_ZONE=UTC")
cursor = conn.cursor()
cursor.execute("select to_char(current_date) from dual")
result, = cursor.fetchone()
print("main(): result is", repr(result))
conn.close()
# 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")
conn = pool.acquire(tag="NLS_DATE_FORMAT=FULL;TIME_ZONE=MST", matchanytag=True)
cursor = conn.cursor()
cursor.execute("select to_char(current_date) from dual")
result, = cursor.fetchone()
print("main(): result is", repr(result))
conn.close()
# acquire session and display results from PL/SQL session logs
conn = pool.acquire()
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)

View File

@ -1,36 +0,0 @@
#------------------------------------------------------------------------------
# Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
#------------------------------------------------------------------------------
#------------------------------------------------------------------------------
# SetupSamples.py
#
# Creates users and populates their schemas with the tables and packages
# necessary for the cx_Oracle samples. An edition is also created for the
# demonstration of PL/SQL editioning.
#------------------------------------------------------------------------------
from __future__ import print_function
import cx_Oracle
import SampleEnv
import DropSamples
# connect as SYSDBA
conn = cx_Oracle.connect(SampleEnv.GetSysdbaConnectString(),
mode = cx_Oracle.SYSDBA)
# drop existing users and editions, if applicable
DropSamples.DropSamples(conn)
# create sample schema and edition
print("Creating sample schemas and edition...")
SampleEnv.RunSqlScript(conn, "SetupSamples",
main_user = SampleEnv.GetMainUser(),
main_password = SampleEnv.GetMainPassword(),
edition_user = SampleEnv.GetEditionUser(),
edition_password = SampleEnv.GetEditionPassword(),
edition_name = SampleEnv.GetEditionName())
print("Done.")

View File

@ -1,67 +0,0 @@
#------------------------------------------------------------------------------
# Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved.
#------------------------------------------------------------------------------
#------------------------------------------------------------------------------
# Threads.py
# This script demonstrates the use of threads with cx_Oracle. A session pool
# is used so that multiple connections are available to perform work on the
# database. Only one operation (such as an execute or fetch) can take place at
# a time on a connection. In the below example, one of the threads performs
# dbms_lock.sleep while the other performs a query.
#
# This script requires cx_Oracle 2.5 and higher.
#------------------------------------------------------------------------------
from __future__ import print_function
import cx_Oracle
import SampleEnv
import threading
pool = cx_Oracle.SessionPool(SampleEnv.GetMainUser(),
SampleEnv.GetMainPassword(), SampleEnv.GetConnectString(), min=2,
max=5, increment=1, threaded=True)
def TheLongQuery():
conn = pool.acquire()
cursor = conn.cursor()
cursor.arraysize = 25000
print("TheLongQuery(): beginning execute...")
cursor.execute("""
select *
from
TestNumbers
cross join TestNumbers
cross join TestNumbers
cross join TestNumbers
cross join TestNumbers
cross join TestNumbers""")
print("TheLongQuery(): done execute...")
while True:
rows = cursor.fetchmany()
if not rows:
break
print("TheLongQuery(): fetched", len(rows), "rows...")
print("TheLongQuery(): all done!")
def DoALock():
conn = pool.acquire()
cursor = conn.cursor()
print("DoALock(): beginning execute...")
cursor.callproc("dbms_lock.sleep", (5,))
print("DoALock(): done execute...")
thread1 = threading.Thread(None, TheLongQuery)
thread1.start()
thread2 = threading.Thread(None, DoALock)
thread2.start()
thread1.join()
thread2.join()
print("All done!")

View File

@ -1,5 +1,5 @@
#------------------------------------------------------------------------------
# Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
# Copyright (c) 2016, 2021, Oracle and/or its affiliates. All rights reserved.
#
# Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved.
#
@ -8,7 +8,7 @@
#------------------------------------------------------------------------------
#------------------------------------------------------------------------------
# AppContext.py
# 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().
@ -16,10 +16,8 @@
# This script requires cx_Oracle 5.3 and higher.
#------------------------------------------------------------------------------
from __future__ import print_function
import cx_Oracle
import SampleEnv
import cx_Oracle as oracledb
import sample_env
# define constants used throughout the script; adjust as desired
APP_CTX_NAMESPACE = "CLIENTCONTEXT"
@ -29,11 +27,10 @@ APP_CTX_ENTRIES = [
( APP_CTX_NAMESPACE, "ATTR3", "VALUE3" )
]
connection = cx_Oracle.connect(SampleEnv.GetMainConnectString(),
appcontext = APP_CTX_ENTRIES)
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)

View File

@ -1,39 +1,39 @@
#------------------------------------------------------------------------------
# Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved.
# Copyright (c) 2018, 2021, Oracle and/or its affiliates. All rights reserved.
#------------------------------------------------------------------------------
#------------------------------------------------------------------------------
# AdvancedQueuingNotification.py
# 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
# "BOOKS" queue. This is most easily accomplished by running the
# AdvancedQueuing sample.
# "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.
#------------------------------------------------------------------------------
from __future__ import print_function
import cx_Oracle
import SampleEnv
import threading
import time
import cx_Oracle as oracledb
import sample_env
registered = True
def callback(message):
def process_messages(message):
global registered
print("Message type:", message.type)
if message.type == cx_Oracle.EVENT_DEREG:
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 = cx_Oracle.connect(SampleEnv.GetMainConnectString(), events = True)
sub = connection.subscribe(namespace = cx_Oracle.SUBSCR_NAMESPACE_AQ,
name = "BOOKS", callback = callback, timeout = 300)
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)
@ -44,4 +44,3 @@ print("--> Timeout:", sub.timeout)
while registered:
print("Waiting for notifications....")
time.sleep(5)

View File

@ -1,9 +1,9 @@
#------------------------------------------------------------------------------
# Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
# Copyright (c) 2016, 2021, Oracle and/or its affiliates. All rights reserved.
#------------------------------------------------------------------------------
#------------------------------------------------------------------------------
# ArrayDMLRowCounts.py
# 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.
@ -13,39 +13,36 @@
# This script requires cx_Oracle 5.2 and higher.
#------------------------------------------------------------------------------
from __future__ import print_function
import cx_Oracle as oracledb
import sample_env
import cx_Oracle
import SampleEnv
connection = cx_Oracle.connect(SampleEnv.GetMainConnectString())
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 parentId, count in cursor.execute("""
for parent_id, count in cursor.execute("""
select ParentId, count(*)
from ChildTable
group by ParentId
order by ParentId"""):
print("Parent ID:", parentId, "has", int(count), "rows.")
print("Parent ID:", parent_id, "has", int(count), "rows.")
print()
# delete the following parent IDs only
parentIdsToDelete = [20, 30, 50]
parent_ids_to_delete = [20, 30, 50]
print("Deleting Parent IDs:", parentIdsToDelete)
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 parentIdsToDelete],
[(i,) for i in parent_ids_to_delete],
arraydmlrowcounts = True)
# display the number of rows deleted for each parent ID
rowCounts = cursor.getarraydmlrowcounts()
for parentId, count in zip(parentIdsToDelete, rowCounts):
print("Parent ID:", parentId, "deleted", count, "rows.")
row_counts = cursor.getarraydmlrowcounts()
for parent_id, count in zip(parent_ids_to_delete, row_counts):
print("Parent ID:", parent_id, "deleted", count, "rows.")

View File

@ -1,9 +1,9 @@
#------------------------------------------------------------------------------
# Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
# Copyright (c) 2016, 2021, Oracle and/or its affiliates. All rights reserved.
#------------------------------------------------------------------------------
#------------------------------------------------------------------------------
# BatchErrors.py
# batch_errors.py
#
# Demonstrate the use of the Oracle Database 12.1 feature that allows
# cursor.executemany() to complete successfully, even if errors take
@ -15,16 +15,14 @@
# This script requires cx_Oracle 5.2 and higher.
#------------------------------------------------------------------------------
from __future__ import print_function
import cx_Oracle as oracledb
import sample_env
import cx_Oracle
import SampleEnv
connection = cx_Oracle.connect(SampleEnv.GetMainConnectString())
connection = oracledb.connect(sample_env.get_main_connect_string())
cursor = connection.cursor()
# define data to insert
dataToInsert = [
data_to_insert = [
(1016, 10, 'Child B of Parent 10'),
(1017, 10, 'Child C of Parent 10'),
(1018, 20, 'Child D of Parent 20'),
@ -41,15 +39,15 @@ cursor.execute("""
from ChildTable""")
count, = cursor.fetchone()
print("number of rows in child table:", int(count))
print("number of rows to insert:", len(dataToInsert))
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)",
dataToInsert)
except cx_Oracle.DatabaseError as e:
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)
@ -66,12 +64,12 @@ 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)", dataToInsert,
batcherrors = True, arraydmlrowcounts = True)
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
rowCounts = cursor.getarraydmlrowcounts()
print("Array DML row counts:", rowCounts)
row_counts = cursor.getarraydmlrowcounts()
print("Array DML row counts:", row_counts)
# display the errors that have taken place
errors = cursor.getbatcherrors()

75
samples/bind_insert.py Normal file
View File

@ -0,0 +1,75 @@
#------------------------------------------------------------------------------
# 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)

View File

@ -1,9 +1,9 @@
#------------------------------------------------------------------------------
# Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
# Copyright (c) 2016, 2021, Oracle and/or its affiliates. All rights reserved.
#------------------------------------------------------------------------------
#------------------------------------------------------------------------------
# BindQuery.py
# 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
@ -12,12 +12,10 @@
# special characters or SQL injection attacks.
#------------------------------------------------------------------------------
from __future__ import print_function
import cx_Oracle as oracledb
import sample_env
import cx_Oracle
import SampleEnv
connection = cx_Oracle.connect(SampleEnv.GetMainConnectString())
connection = oracledb.connect(sample_env.get_main_connect_string())
cursor = connection.cursor()
sql = 'select * from SampleQueryTab where id = :bvid'
@ -31,4 +29,3 @@ print("Query results with id = 1")
for row in cursor.execute(sql, bvid = 1):
print(row)
print()

View File

@ -1,5 +1,5 @@
#------------------------------------------------------------------------------
# Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
# Copyright (c) 2019, 2021, Oracle and/or its affiliates. All rights reserved.
#
# Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved.
#
@ -8,18 +8,16 @@
#------------------------------------------------------------------------------
#------------------------------------------------------------------------------
# BulkAQ.py
# bulk_aq.py
# This script demonstrates how to use bulk enqueuing and dequeuing of
# messages with advanced queuing using cx_Oracle. It makes use of a RAW queue
# created in the sample setup.
# messages with advanced queuing. It makes use of a RAW queue created in the
# sample setup.
#
# This script requires cx_Oracle 7.2 and higher.
# This script requires cx_Oracle 8.2 and higher.
#------------------------------------------------------------------------------
from __future__ import print_function
import cx_Oracle
import SampleEnv
import cx_Oracle as oracledb
import sample_env
QUEUE_NAME = "DEMO_RAW_QUEUE"
PAYLOAD_DATA = [
@ -38,37 +36,37 @@ PAYLOAD_DATA = [
]
# connect to database
connection = cx_Oracle.connect(SampleEnv.GetMainConnectString())
connection = oracledb.connect(sample_env.get_main_connect_string())
cursor = connection.cursor()
# create queue
queue = connection.queue(QUEUE_NAME)
queue.deqOptions.wait = cx_Oracle.DEQ_NO_WAIT
queue.deqOptions.navigation = cx_Oracle.DEQ_FIRST_MSG
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():
while queue.deqone():
pass
# enqueue a few messages
print("Enqueuing messages...")
batchSize = 6
dataToEnq = PAYLOAD_DATA
while dataToEnq:
batchData = dataToEnq[:batchSize]
dataToEnq = dataToEnq[batchSize:]
messages = [connection.msgproperties(payload=d) for d in batchData]
for data in batchData:
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)
queue.enqmany(messages)
connection.commit()
# dequeue the messages
print("\nDequeuing messages...")
batchSize = 8
batch_size = 8
while True:
messages = queue.deqMany(batchSize)
messages = queue.deqmany(batch_size)
if not messages:
break
for props in messages:

View File

@ -1,9 +1,9 @@
#------------------------------------------------------------------------------
# Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
# Copyright (c) 2019, 2021, Oracle and/or its affiliates. All rights reserved.
#------------------------------------------------------------------------------
#------------------------------------------------------------------------------
# CallTimeout.py
# 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)
@ -13,27 +13,29 @@
# higher.
#------------------------------------------------------------------------------
from __future__ import print_function
import cx_Oracle as oracledb
import sample_env
import cx_Oracle
import SampleEnv
connection = cx_Oracle.connect(SampleEnv.GetMainConnectString())
connection.callTimeout = 2000
print("Call timeout set at", connection.callTimeout, "milliseconds...")
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("dbms_lock.sleep", (3,))
except cx_Oracle.DatabaseError as e:
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)

View File

@ -0,0 +1,78 @@
#------------------------------------------------------------------------------
# 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!")

View File

@ -1,5 +1,5 @@
#------------------------------------------------------------------------------
# Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
# Copyright (c) 2016, 2021, Oracle and/or its affiliates. All rights reserved.
#
# Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved.
#
@ -8,22 +8,20 @@
#------------------------------------------------------------------------------
#------------------------------------------------------------------------------
# CQN.py
# 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
# cx_Oracle.TestTempTable and you will see the notification of that change.
# TestTempTable and you will see the notification of that change.
#
# This script requires cx_Oracle 5.3 and higher.
#------------------------------------------------------------------------------
from __future__ import print_function
import cx_Oracle
import SampleEnv
import threading
import time
import cx_Oracle as oracledb
import sample_env
registered = True
def callback(message):
@ -50,9 +48,10 @@ def callback(message):
print("-" * 60)
print("=" * 60)
connection = cx_Oracle.connect(SampleEnv.GetMainConnectString(), events = True)
sub = connection.subscribe(callback = callback, timeout = 1800,
qos = cx_Oracle.SUBSCR_QOS_QUERY | cx_Oracle.SUBSCR_QOS_ROWIDS)
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)
@ -60,11 +59,10 @@ print("--> Namespace:", sub.namespace)
print("--> Protocol:", sub.protocol)
print("--> Timeout:", sub.timeout)
print("--> Operations:", sub.operations)
print("--> Rowids?:", bool(sub.qos & cx_Oracle.SUBSCR_QOS_ROWIDS))
queryId = sub.registerquery("select * from TestTempTable")
print("Registered query:", queryId)
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)

74
samples/cqn2.py Normal file
View File

@ -0,0 +1,74 @@
#------------------------------------------------------------------------------
# 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)

View File

@ -1,5 +1,5 @@
#------------------------------------------------------------------------------
# Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
# Copyright (c) 2016, 2021, Oracle and/or its affiliates. All rights reserved.
#
# Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved.
#
@ -8,22 +8,20 @@
#------------------------------------------------------------------------------
#------------------------------------------------------------------------------
# DatabaseChangeNotification.py
# 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
# cx_Oracle.TestTempTable and you will see the notification of that change.
# TestTempTable and you will see the notification of that change.
#
# This script requires cx_Oracle 5.3 and higher.
#------------------------------------------------------------------------------
from __future__ import print_function
import cx_Oracle
import SampleEnv
import threading
import time
import cx_Oracle as oracledb
import sample_env
registered = True
def callback(message):
@ -47,9 +45,10 @@ def callback(message):
print("-" * 60)
print("=" * 60)
connection = cx_Oracle.connect(SampleEnv.GetMainConnectString(), events = True)
sub = connection.subscribe(callback = callback, timeout = 1800,
qos = cx_Oracle.SUBSCR_QOS_ROWIDS)
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)
@ -58,10 +57,9 @@ print("--> Namespace:", sub.namespace)
print("--> Protocol:", sub.protocol)
print("--> Timeout:", sub.timeout)
print("--> Operations:", sub.operations)
print("--> Rowids?:", bool(sub.qos & cx_Oracle.SUBSCR_QOS_ROWIDS))
print("--> Rowids?:", bool(sub.qos & oracledb.SUBSCR_QOS_ROWIDS))
sub.registerquery("select * from TestTempTable")
while registered:
print("Waiting for notifications....")
time.sleep(5)

View File

@ -1,5 +1,5 @@
#------------------------------------------------------------------------------
# Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
# Copyright (c) 2016, 2021, Oracle and/or its affiliates. All rights reserved.
#
# Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved.
#
@ -8,22 +8,22 @@
#------------------------------------------------------------------------------
#------------------------------------------------------------------------------
# DatabaseShutdown.py
# This script demonstrates shutting down a database using Python. It is only
# possible in Oracle 10g Release 2 and higher. The connection used assumes that
# the environment variable ORACLE_SID has been set.
# 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
import cx_Oracle as oracledb
# need to connect as SYSDBA or SYSOPER
connection = cx_Oracle.connect("/", mode = cx_Oracle.SYSDBA)
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 = cx_Oracle.DBSHUTDOWN_IMMEDIATE)
connection.shutdown(mode=oracledb.DBSHUTDOWN_IMMEDIATE)
# now close and dismount the database
cursor = connection.cursor()
@ -31,5 +31,4 @@ cursor.execute("alter database close normal")
cursor.execute("alter database dismount")
# perform the final shutdown call
connection.shutdown(mode = cx_Oracle.DBSHUTDOWN_FINAL)
connection.shutdown(mode=oracledb.DBSHUTDOWN_FINAL)

View File

@ -1,5 +1,5 @@
#------------------------------------------------------------------------------
# Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
# Copyright (c) 2016, 2021, Oracle and/or its affiliates. All rights reserved.
#
# Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved.
#
@ -8,24 +8,22 @@
#------------------------------------------------------------------------------
#------------------------------------------------------------------------------
# DatabaseStartup.py
# This script demonstrates starting up a database using Python. It is only
# possible in Oracle 10g Release 2 and higher. The connection used assumes that
# the environment variable ORACLE_SID has been set.
# 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
import cx_Oracle as oracledb
# the connection must be in PRELIM_AUTH mode
connection = cx_Oracle.connect("/",
mode = cx_Oracle.SYSDBA | cx_Oracle.PRELIM_AUTH)
connection = oracledb.connect(mode=oracledb.SYSDBA | oracledb.PRELIM_AUTH)
connection.startup()
# the following statements must be issued in normal SYSDBA mode
connection = cx_Oracle.connect("/", mode = cx_Oracle.SYSDBA)
connection = oracledb.connect("/", mode=oracledb.SYSDBA)
cursor = connection.cursor()
cursor.execute("alter database mount")
cursor.execute("alter database open")

44
samples/dbms_output.py Normal file
View File

@ -0,0 +1,44 @@
#------------------------------------------------------------------------------
# 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

View File

@ -1,5 +1,5 @@
#------------------------------------------------------------------------------
# Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved.
# Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved.
#
# Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved.
#
@ -8,21 +8,18 @@
#------------------------------------------------------------------------------
#------------------------------------------------------------------------------
# DMLReturningMultipleRows.py
# 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.
#------------------------------------------------------------------------------
from __future__ import print_function
import cx_Oracle
import datetime
import SampleEnv
import cx_Oracle as oracledb
import sample_env
# truncate table first so that script can be rerun
connection = cx_Oracle.connect(SampleEnv.GetMainConnectString())
connection = oracledb.connect(sample_env.get_main_connect_string())
cursor = connection.cursor()
print("Truncating table...")
cursor.execute("truncate table TestTempTable")
@ -34,15 +31,14 @@ for i in range(5):
cursor.execute("insert into TestTempTable values (:1, :2)", data)
# now delete them and use DML returning to return the data that was inserted
intCol = cursor.var(int)
stringCol = cursor.var(str)
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 :intCol, :stringCol""",
intCol = intCol,
stringCol = stringCol)
returning IntCol, StringCol into :int_col, :string_col""",
int_col=int_col,
string_col=string_col)
print("Data returned:")
for intVal, stringVal in zip(intCol.getvalue(), stringCol.getvalue()):
print(tuple([intVal, stringVal]))
for int_val, string_val in zip(int_col.getvalue(), string_col.getvalue()):
print(tuple([int_val, string_val]))

View File

@ -1,5 +1,5 @@
#------------------------------------------------------------------------------
# Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
# Copyright (c) 2016, 2021, Oracle and/or its affiliates. All rights reserved.
#
# Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved.
#
@ -8,7 +8,7 @@
#------------------------------------------------------------------------------
#------------------------------------------------------------------------------
# DRCP.py
# 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
@ -25,18 +25,19 @@
# There is no difference in how a connection is used once it has been
# established.
#
# This script requires cx_Oracle 5.0 and higher.
# 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.
#
#------------------------------------------------------------------------------
from __future__ import print_function
import cx_Oracle as oracledb
import sample_env
import cx_Oracle
import SampleEnv
conn = cx_Oracle.connect(SampleEnv.GetDrcpConnectString(), cclass = "PYCLASS",
purity = cx_Oracle.ATTR_PURITY_SELF)
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)

24
samples/drop_samples.py Normal file
View File

@ -0,0 +1,24 @@
#------------------------------------------------------------------------------
# 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.")

View File

@ -1,5 +1,5 @@
#------------------------------------------------------------------------------
# Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
# Copyright (c) 2016, 2021, Oracle and/or its affiliates. All rights reserved.
#
# Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved.
#
@ -8,7 +8,7 @@
#------------------------------------------------------------------------------
#------------------------------------------------------------------------------
# Editioning.py
# 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
@ -17,17 +17,16 @@
# This script requires cx_Oracle 5.3 and higher.
#------------------------------------------------------------------------------
from __future__ import print_function
import cx_Oracle
import SampleEnv
import os
import cx_Oracle as oracledb
import sample_env
# connect to the editions user and create a procedure
editionConnectString = SampleEnv.GetEditionConnectString()
connection = cx_Oracle.connect(editionConnectString)
print("Edition should be None, actual value is:",
repr(connection.edition))
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
@ -36,12 +35,12 @@ cursor.execute("""
end;""")
result = cursor.callfunc("TestEditions", str)
print("Function should return 'Base Procedure', actually returns:",
repr(result))
repr(result))
# next, change the edition and recreate the procedure in the new edition
cursor.execute("alter session set edition = %s" % SampleEnv.GetEditionName())
print("Edition should be", repr(SampleEnv.GetEditionName().upper()),
"actual value is:", repr(connection.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
@ -49,30 +48,29 @@ cursor.execute("""
end;""")
result = cursor.callfunc("TestEditions", str)
print("Function should return 'Edition 1 Procedure', actually returns:",
repr(result))
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))
repr(result))
# the edition can be set upon connection
connection = cx_Oracle.connect(editionConnectString,
edition = SampleEnv.GetEditionName().upper())
connection = oracledb.connect(edition_connect_string,
edition=edition_name.upper())
cursor = connection.cursor()
result = cursor.callfunc("TestEditions", str)
print("Function should return 'Edition 1 Procedure', actually returns:",
repr(result))
repr(result))
# it can also be set via the environment variable ORA_EDITION
os.environ["ORA_EDITION"] = SampleEnv.GetEditionName().upper()
connection = cx_Oracle.connect(editionConnectString)
print("Edition should be", repr(SampleEnv.GetEditionName().upper()),
"actual value is:", repr(connection.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))
repr(result))

View File

@ -1,41 +1,40 @@
#------------------------------------------------------------------------------
# Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
# Copyright (c) 2016, 2021, Oracle and/or its affiliates. All rights reserved.
#------------------------------------------------------------------------------
#------------------------------------------------------------------------------
# GenericRowFactory.py
# generic_row_factory.py
#
# Demonstrate the ability to return named tuples for all queries using a
# subclassed cursor and row factory.
#------------------------------------------------------------------------------
from __future__ import print_function
import collections
import cx_Oracle
import SampleEnv
class Connection(cx_Oracle.Connection):
import cx_Oracle as oracledb
import sample_env
class Connection(oracledb.Connection):
def cursor(self):
return Cursor(self)
class Cursor(cx_Oracle.Cursor):
class Cursor(oracledb.Cursor):
def execute(self, statement, args = None):
prepareNeeded = (self.statement != statement)
result = super(Cursor, self).execute(statement, args or [])
if prepareNeeded:
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:
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(SampleEnv.GetMainConnectString())
connection = Connection(sample_env.get_main_connect_string())
cursor = connection.cursor()
# the names are now available directly for each query executed
@ -46,4 +45,3 @@ print()
for row in cursor.execute("select ChildId, Description from ChildTable"):
print(row.CHILDID, "->", row.DESCRIPTION)
print()

View File

@ -1,5 +1,5 @@
#------------------------------------------------------------------------------
# Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
# Copyright (c) 2016, 2021, Oracle and/or its affiliates. All rights reserved.
#
# Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved.
#
@ -8,7 +8,7 @@
#------------------------------------------------------------------------------
#------------------------------------------------------------------------------
# ImplicitResults.py
# 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.
@ -16,12 +16,10 @@
# This script requires cx_Oracle 5.3 and higher.
#------------------------------------------------------------------------------
from __future__ import print_function
import cx_Oracle as oracledb
import sample_env
import cx_Oracle
import SampleEnv
connection = cx_Oracle.connect(SampleEnv.GetMainConnectString())
connection = oracledb.connect(sample_env.get_main_connect_string())
cursor = connection.cursor()
# use PL/SQL block to return two cursors
@ -44,9 +42,8 @@ cursor.execute("""
end;""")
# display results
for ix, resultSet in enumerate(cursor.getimplicitresults()):
for ix, result_set in enumerate(cursor.getimplicitresults()):
print("Result Set #" + str(ix + 1))
for row in resultSet:
for row in result_set:
print(row)
print()

View File

@ -1,5 +1,5 @@
#------------------------------------------------------------------------------
# Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
# Copyright (c) 2016, 2021, Oracle and/or its affiliates. All rights reserved.
#
# Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved.
#
@ -8,28 +8,26 @@
#------------------------------------------------------------------------------
#------------------------------------------------------------------------------
# InsertGeometry.py
# 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.
#------------------------------------------------------------------------------
from __future__ import print_function
import cx_Oracle
import SampleEnv
import cx_Oracle as oracledb
import sample_env
# create and populate Oracle objects
connection = cx_Oracle.connect(SampleEnv.GetMainConnectString())
typeObj = connection.gettype("MDSYS.SDO_GEOMETRY")
elementInfoTypeObj = connection.gettype("MDSYS.SDO_ELEM_INFO_ARRAY")
ordinateTypeObj = connection.gettype("MDSYS.SDO_ORDINATE_ARRAY")
obj = typeObj.newobject()
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 = elementInfoTypeObj.newobject()
obj.SDO_ELEM_INFO = element_info_type_obj.newobject()
obj.SDO_ELEM_INFO.extend([1, 1003, 3])
obj.SDO_ORDINATES = ordinateTypeObj.newobject()
obj.SDO_ORDINATES = ordinate_type_obj.newobject()
obj.SDO_ORDINATES.extend([1, 1, 5, 7])
print("Created object", obj)
@ -52,7 +50,6 @@ if count == 0:
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)
cursor.execute("insert into TestGeometry values (1, :obj)", obj=obj)
connection.commit()
print("Success!")

89
samples/json_blob.py Normal file
View File

@ -0,0 +1,89 @@
#------------------------------------------------------------------------------
# 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)

94
samples/json_direct.py Normal file
View File

@ -0,0 +1,94 @@
#------------------------------------------------------------------------------
# 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)

56
samples/last_rowid.py Normal file
View File

@ -0,0 +1,56 @@
#------------------------------------------------------------------------------
# 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()

View File

@ -0,0 +1,67 @@
#------------------------------------------------------------------------------
# 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.")

View File

@ -1,5 +1,5 @@
#------------------------------------------------------------------------------
# Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
# Copyright (c) 2016, 2021, Oracle and/or its affiliates. All rights reserved.
#
# Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved.
#
@ -8,20 +8,18 @@
#------------------------------------------------------------------------------
#------------------------------------------------------------------------------
# ObjectAQ.py
# This script demonstrates how to use advanced queuing with objects using
# cx_Oracle. It makes use of a simple type and queue created in the sample
# setup.
# 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 7.2 and higher.
# This script requires cx_Oracle 8.2 and higher.
#------------------------------------------------------------------------------
from __future__ import print_function
import cx_Oracle
import SampleEnv
import decimal
import cx_Oracle as oracledb
import sample_env
BOOK_TYPE_NAME = "UDT_BOOK"
QUEUE_NAME = "DEMO_BOOK_QUEUE"
BOOK_DATA = [
@ -32,35 +30,35 @@ BOOK_DATA = [
]
# connect to database
connection = cx_Oracle.connect(SampleEnv.GetMainConnectString())
connection = oracledb.connect(sample_env.get_main_connect_string())
cursor = connection.cursor()
# create queue
booksType = connection.gettype(BOOK_TYPE_NAME)
queue = connection.queue(QUEUE_NAME, booksType)
queue.deqOptions.wait = cx_Oracle.DEQ_NO_WAIT
queue.deqOptions.navigation = cx_Oracle.DEQ_FIRST_MSG
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():
while queue.deqone():
pass
# enqueue a few messages
print("Enqueuing messages...")
for title, authors, price in BOOK_DATA:
book = booksType.newobject()
book = books_type.newobject()
book.TITLE = title
book.AUTHORS = authors
book.PRICE = price
print(title)
queue.enqOne(connection.msgproperties(payload=book))
queue.enqone(connection.msgproperties(payload=book))
connection.commit()
# dequeue the messages
print("\nDequeuing messages...")
while True:
props = queue.deqOne()
props = queue.deqone()
if not props:
break
print(props.payload.TITLE)

View File

@ -1,9 +1,9 @@
#------------------------------------------------------------------------------
# Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
# Copyright (c) 2016, 2021, Oracle and/or its affiliates. All rights reserved.
#------------------------------------------------------------------------------
#------------------------------------------------------------------------------
# PLSQLCollection.py
# plsql_collection.py
#
# Demonstrate how to get the value of a PL/SQL collection from a stored
# procedure.
@ -13,17 +13,15 @@
# is new in cx_Oracle 7.0.
#------------------------------------------------------------------------------
from __future__ import print_function
import cx_Oracle as oracledb
import sample_env
import cx_Oracle
import SampleEnv
connection = cx_Oracle.connect(SampleEnv.GetMainConnectString())
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
typeObj = connection.gettype("PKG_DEMO.UDT_STRINGLIST")
obj = typeObj.newobject()
type_obj = connection.gettype("PKG_DEMO.UDT_STRINGLIST")
obj = type_obj.newobject()
# call the stored procedure which will populate the object
cursor = connection.cursor()
@ -46,4 +44,3 @@ print()
print("Values of collection as dictionary:")
print(obj.asdict())
print()

View File

@ -1,21 +1,18 @@
#------------------------------------------------------------------------------
# Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
# Copyright (c) 2016, 2021, Oracle and/or its affiliates. All rights reserved.
#------------------------------------------------------------------------------
#------------------------------------------------------------------------------
# PLSQLFunction.py
# plsql_function.py
#
# Demonstrate how to call a PL/SQL function and get its return value.
#------------------------------------------------------------------------------
from __future__ import print_function
import cx_Oracle as oracledb
import sample_env
import cx_Oracle
import SampleEnv
connection = cx_Oracle.connect(SampleEnv.GetMainConnectString())
connection = oracledb.connect(sample_env.get_main_connect_string())
cursor = connection.cursor()
res = cursor.callfunc('myfunc', int, ('abc', 2))
print(res)

View File

@ -1,23 +1,20 @@
#------------------------------------------------------------------------------
# Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
# Copyright (c) 2016, 2021, Oracle and/or its affiliates. All rights reserved.
#------------------------------------------------------------------------------
#------------------------------------------------------------------------------
# PLSQLProcedure.py
# plsql_procedure.py
#
# Demonstrate how to call a PL/SQL stored procedure and get the results of an
# OUT variable.
#------------------------------------------------------------------------------
from __future__ import print_function
import cx_Oracle as oracledb
import sample_env
import cx_Oracle
import SampleEnv
connection = cx_Oracle.connect(SampleEnv.GetMainConnectString())
connection = oracledb.connect(sample_env.get_main_connect_string())
cursor = connection.cursor()
myvar = cursor.var(int)
cursor.callproc('myproc', (123, myvar))
print(myvar.getvalue())

View File

@ -1,29 +1,27 @@
#------------------------------------------------------------------------------
# Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
# Copyright (c) 2016, 2021, Oracle and/or its affiliates. All rights reserved.
#------------------------------------------------------------------------------
#------------------------------------------------------------------------------
# PLSQLRecord.py
# plsql_record.py
#
# Demonstrate how to bind (in and out) a PL/SQL record.
#
# This feature is new in cx_Oracle 5.3 and is only available in Oracle
# Database 12.1 and higher.
# This feature is only available in Oracle Database 12.1 and higher.
#------------------------------------------------------------------------------
from __future__ import print_function
import cx_Oracle
import SampleEnv
import datetime
connection = cx_Oracle.connect(SampleEnv.GetMainConnectString())
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
typeObj = connection.gettype("PKG_DEMO.UDT_DEMORECORD")
obj = typeObj.newobject()
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)
@ -46,4 +44,3 @@ print("STRINGVALUE ->", obj.STRINGVALUE)
print("DATEVALUE ->", obj.DATEVALUE)
print("BOOLEANVALUE ->", obj.BOOLEANVALUE)
print()

View File

@ -1,19 +1,17 @@
#------------------------------------------------------------------------------
# Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
# Copyright (c) 2016, 2021, Oracle and/or its affiliates. All rights reserved.
#------------------------------------------------------------------------------
#------------------------------------------------------------------------------
# Query.py
# query.py
#
# Demonstrate how to perform a query in different ways.
#------------------------------------------------------------------------------
from __future__ import print_function
import cx_Oracle as oracledb
import sample_env
import cx_Oracle
import SampleEnv
connection = cx_Oracle.connect(SampleEnv.GetMainConnectString())
connection = oracledb.connect(sample_env.get_main_connect_string())
sql = """
select * from SampleQueryTab
@ -36,6 +34,13 @@ print()
print("Fetch many rows")
cursor.execute(sql)
res = cursor.fetchmany(numRows=3)
res = cursor.fetchmany(3)
print(res)
print()
print("Fetch each row as a Dictionary")
cursor.execute(sql)
columns = [col[0] for col in cursor.description]
cursor.rowfactory = lambda *args: dict(zip(columns, args))
for row in cursor:
print(row)

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