Compare commits
320 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
70cbc43752 | ||
|
|
bed2c037f9 | ||
|
|
93d69d861b | ||
|
|
83774f9520 | ||
|
|
6766bcaf27 | ||
|
|
a05b9a5233 | ||
|
|
5cfbb7d9e4 | ||
|
|
fde577bf1f | ||
|
|
1ad43aa912 | ||
|
|
3db3e3772e | ||
|
|
59c41535e3 | ||
|
|
5728cf534e | ||
|
|
3a40eeacaa | ||
|
|
fde9ec78fc | ||
|
|
45118e0c31 | ||
|
|
29ca919445 | ||
|
|
ae687ce736 | ||
|
|
2d33fec37a | ||
|
|
9db9d6907a | ||
|
|
00dc44eade | ||
|
|
76157dd28a | ||
|
|
83148ec574 | ||
|
|
eb37d27464 | ||
|
|
05a9097847 | ||
|
|
438c885c20 | ||
|
|
1347b04976 | ||
|
|
702a91be51 | ||
|
|
08117db459 | ||
|
|
440163efe5 | ||
|
|
c665d2efca | ||
|
|
dfeebc3358 | ||
|
|
5cce9efd49 | ||
|
|
cc067bf83e | ||
|
|
abb666706b | ||
|
|
4229a6d8ad | ||
|
|
59558714ac | ||
|
|
a61b14a5c6 | ||
|
|
5dccb9fb5f | ||
|
|
206e85e4a2 | ||
|
|
df80067f47 | ||
|
|
d585cf06df | ||
|
|
ce4713c6f7 | ||
|
|
31d94e7bf1 | ||
|
|
94e9489e25 | ||
|
|
46a3d70ad8 | ||
|
|
cd21e92edd | ||
|
|
0633e1017c | ||
|
|
cfa2750854 | ||
|
|
d5461bd008 | ||
|
|
07bb84e1b6 | ||
|
|
4ed562c205 | ||
|
|
97be497fc9 | ||
|
|
c665e7a9e2 | ||
|
|
89eaea4f42 | ||
|
|
fbd9bfafb6 | ||
|
|
4661c6690f | ||
|
|
4189983481 | ||
|
|
5680620f02 | ||
|
|
932b413226 | ||
|
|
89aea21176 | ||
|
|
2c6468d992 | ||
|
|
8c99d1ddde | ||
|
|
96f938286d | ||
|
|
ab6e6f06ef | ||
|
|
fa36066875 | ||
|
|
ba6e054a24 | ||
|
|
82fb398b38 | ||
|
|
fa8d6fa5be | ||
|
|
cccfa322c7 | ||
|
|
0edb436abf | ||
|
|
9e8bc6676c | ||
|
|
7ad13e73d3 | ||
|
|
cb1061a553 | ||
|
|
df89702a4e | ||
|
|
c527c06650 | ||
|
|
4b72d0de02 | ||
|
|
74d16d8962 | ||
|
|
a1f8e1ffff | ||
|
|
5e2a363ac5 | ||
|
|
755b6fa861 | ||
|
|
4264d7bc5d | ||
|
|
250b07299d | ||
|
|
10f1c48289 | ||
|
|
95baec2436 | ||
|
|
ffa2086fd3 | ||
|
|
c9110a33b8 | ||
|
|
517868eaf9 | ||
|
|
74b2e9a258 | ||
|
|
658f4cd3c2 | ||
|
|
8f901abce7 | ||
|
|
bdf6b4ee4c | ||
|
|
1a42fb1de0 | ||
|
|
e1b05d5095 | ||
|
|
fb2668e87c | ||
|
|
0a3aae5de8 | ||
|
|
5f60401c4a | ||
|
|
bf381478c4 | ||
|
|
aa2e258c98 | ||
|
|
188608e748 | ||
|
|
0ff1003203 | ||
|
|
7382d035f4 | ||
|
|
8ea16b1345 | ||
|
|
c92d251677 | ||
|
|
fa5fc20997 | ||
|
|
9f32c9073e | ||
|
|
543c1b9d99 | ||
|
|
b199c5d57f | ||
|
|
3b8c9cbd5f | ||
|
|
5c41ce1f05 | ||
|
|
86c106dbf4 | ||
|
|
d9cb6b1e56 | ||
|
|
f3984cee55 | ||
|
|
3a23957f6d | ||
|
|
30979c6a57 | ||
|
|
6b9a7b011b | ||
|
|
c76492ec08 | ||
|
|
9c9b92a39c | ||
|
|
b59fe3b0be | ||
|
|
9d4973c85d | ||
|
|
320fb1f31d | ||
|
|
216bc42cf4 | ||
|
|
633d1141db | ||
|
|
7e7133b09a | ||
|
|
c8babe7209 | ||
|
|
4dc91f5206 | ||
|
|
9cfe49f125 | ||
|
|
229f9f5533 | ||
|
|
ef3d84318e | ||
|
|
d242bc716d | ||
|
|
2194d81965 | ||
|
|
04fd1a7ad5 | ||
|
|
551be9150e | ||
|
|
6ebd04ea5a | ||
|
|
6ff7caba7c | ||
|
|
0d28ea1e42 | ||
|
|
a18cc34a4b | ||
|
|
b4443c0f9f | ||
|
|
a21e06a60c | ||
|
|
fa626f9001 | ||
|
|
73a5c56bd1 | ||
|
|
769fbcf585 | ||
|
|
f338af9d1c | ||
|
|
d726c8be51 | ||
|
|
f3610cd2b9 | ||
|
|
96f15f48da | ||
|
|
dd492591a3 | ||
|
|
b34f92e77f | ||
|
|
59231532a4 | ||
|
|
bf6318da08 | ||
|
|
7d017d89e5 | ||
|
|
ccda539172 | ||
|
|
b04f538467 | ||
|
|
74a479087f | ||
|
|
10e5c258fe | ||
|
|
8d719c3be4 | ||
|
|
079927c8ce | ||
|
|
3f06ea39a7 | ||
|
|
7df78b98d0 | ||
|
|
631b8729f5 | ||
|
|
3a941687ea | ||
|
|
898b480035 | ||
|
|
8253bd2165 | ||
|
|
f8bcc6f0ec | ||
|
|
0467db9e4b | ||
|
|
5df9a73c5d | ||
|
|
c144216b95 | ||
|
|
4956aee0e5 | ||
|
|
c7a23115ad | ||
|
|
db1b7ae60d | ||
|
|
6e4fb6cf07 | ||
|
|
4646a18165 | ||
|
|
4b6b2c0dcc | ||
|
|
d5144aa58f | ||
|
|
20686a1fc9 | ||
|
|
633371db7d | ||
|
|
5c6f55ef4d | ||
|
|
0b39b9e801 | ||
|
|
f4148aeb6f | ||
|
|
ba141dd799 | ||
|
|
856d0aab76 | ||
|
|
17fd92f8f6 | ||
|
|
ba9a3ea3c2 | ||
|
|
d39ceb3280 | ||
|
|
bd9d7759b2 | ||
|
|
af1281e535 | ||
|
|
528dec6859 | ||
|
|
ca363bed8f | ||
|
|
fbe929d5c8 | ||
|
|
5ad2408a11 | ||
|
|
6dc0263f1f | ||
|
|
c3561f3595 | ||
|
|
54ab4b19f0 | ||
|
|
5071278938 | ||
|
|
c710fb89a6 | ||
|
|
c513b71fe7 | ||
|
|
9377a9a0ef | ||
|
|
3a4ee32022 | ||
|
|
dd31b41036 | ||
|
|
8a301721fb | ||
|
|
52035c3b85 | ||
|
|
4ffbc9cac8 | ||
|
|
9beb4aa907 | ||
|
|
44be257534 | ||
|
|
b897106f00 | ||
|
|
f7b7785e82 | ||
|
|
08346005a7 | ||
|
|
53a9f41ed1 | ||
|
|
31f73b6ea1 | ||
|
|
4874f68712 | ||
|
|
4fd326bb63 | ||
|
|
f743d02857 | ||
|
|
8d18997d26 | ||
|
|
60fa25eaa1 | ||
|
|
e85a03b57b | ||
|
|
3666a4ab44 | ||
|
|
face13b7d4 | ||
|
|
809ead0b9c | ||
|
|
6b73d2223f | ||
|
|
cb1c3aaa73 | ||
|
|
465024c12b | ||
|
|
1ed26d8d86 | ||
|
|
6ca6f2b0c3 | ||
|
|
22642e2826 | ||
|
|
02d336c5e9 | ||
|
|
79902e11e4 | ||
|
|
5f70edd71c | ||
|
|
428746f4de | ||
|
|
2b1bb5c115 | ||
|
|
e6d1a981a0 | ||
|
|
7b144440aa | ||
|
|
0503265184 | ||
|
|
3f27185b9c | ||
|
|
6f12ea7363 | ||
|
|
c447368230 | ||
|
|
efc3160d5f | ||
|
|
07498fefa1 | ||
|
|
19ff7883be | ||
|
|
3b306187b0 | ||
|
|
dad0419003 | ||
|
|
089639c0f9 | ||
|
|
80b9a31fa6 | ||
|
|
fcbf9ff17c | ||
|
|
495541ca20 | ||
|
|
9cb9842696 | ||
|
|
a7b958d68a | ||
|
|
cd9c7e3fa3 | ||
|
|
952580a565 | ||
|
|
d16c939f74 | ||
|
|
13204317d3 | ||
|
|
9b7137fb20 | ||
|
|
e433c720c9 | ||
|
|
ee893d35f4 | ||
|
|
2d0b4895aa | ||
|
|
7becddbe9c | ||
|
|
942f4aa015 | ||
|
|
4e783a9e67 | ||
|
|
a16a944964 | ||
|
|
0ecf3e7187 | ||
|
|
06a336926a | ||
|
|
2cec047870 | ||
|
|
2c9f8d5ca4 | ||
|
|
3696b35254 | ||
|
|
982a21b891 | ||
|
|
e87c09a46d | ||
|
|
65c22693a6 | ||
|
|
f91f907adf | ||
|
|
d526de1893 | ||
|
|
ff25784c21 | ||
|
|
b104d40016 | ||
|
|
e37e502724 | ||
|
|
6bdc8ee465 | ||
|
|
2ba66bbf71 | ||
|
|
d4c2c11517 | ||
|
|
846bfab0d0 | ||
|
|
13d0f4ec08 | ||
|
|
955ef7ecf5 | ||
|
|
39ff9fc189 | ||
|
|
15ce3024ee | ||
|
|
a120647c76 | ||
|
|
3c4d83fe6c | ||
|
|
e6a825db27 | ||
|
|
5f8d24dd24 | ||
|
|
1d0dd29676 | ||
|
|
ae6164fc58 | ||
|
|
e38b4af987 | ||
|
|
df56c7f17f | ||
|
|
d8bde9ca54 | ||
|
|
e0bd74e61b | ||
|
|
923eb054fc | ||
|
|
ee5fe39544 | ||
|
|
c82ae6880d | ||
|
|
3ab21c9447 | ||
|
|
d4498cf9e0 | ||
|
|
04a7dec0d4 | ||
|
|
82097891b3 | ||
|
|
ce511e2fb7 | ||
|
|
1c474bbaab | ||
|
|
b9711f5457 | ||
|
|
f4bdd3fd68 | ||
|
|
883262da07 | ||
|
|
8681366f53 | ||
|
|
f324757909 | ||
|
|
a654befddf | ||
|
|
3af5e46b4b | ||
|
|
096f8d6412 | ||
|
|
e567eb3a58 | ||
|
|
43ebe8afed | ||
|
|
aaa84fe611 | ||
|
|
c1e1659a14 | ||
|
|
f94bd1d8ac | ||
|
|
ad81e94797 | ||
|
|
c112af35cb | ||
|
|
8789c6d2a0 | ||
|
|
948abd9b7c | ||
|
|
0ae5b9e715 | ||
|
|
362aa82ea2 | ||
|
|
1ccaf68369 | ||
|
|
9023609232 | ||
|
|
e3d3764f5f | ||
|
|
386d6dbdba |
10
.github/ISSUE_TEMPLATE/announcements.md
vendored
Normal file
10
.github/ISSUE_TEMPLATE/announcements.md
vendored
Normal 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
80
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal 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
1
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@ -0,0 +1 @@
|
||||
blank_issues_enabled: false
|
||||
21
.github/ISSUE_TEMPLATE/documentation-and-example-improvements.md
vendored
Normal file
21
.github/ISSUE_TEMPLATE/documentation-and-example-improvements.md
vendored
Normal 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.
|
||||
|
||||
-->
|
||||
21
.github/ISSUE_TEMPLATE/enhancement-requests.md
vendored
Normal file
21
.github/ISSUE_TEMPLATE/enhancement-requests.md
vendored
Normal 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.
|
||||
|
||||
-->
|
||||
72
.github/ISSUE_TEMPLATE/general-questions-and-runtime-problems.md
vendored
Normal file
72
.github/ISSUE_TEMPLATE/general-questions-and-runtime-problems.md
vendored
Normal 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
|
||||
```
|
||||
|
||||
-->
|
||||
64
.github/ISSUE_TEMPLATE/installation-questions.md
vendored
Normal file
64
.github/ISSUE_TEMPLATE/installation-questions.md
vendored
Normal 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
6
.github/SUPPORT.md
vendored
@ -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
|
||||
|
||||
|
||||
45
.github/issue_template.md
vendored
45
.github/issue_template.md
vendored
@ -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?
|
||||
21
.github/pull_request_template.md
vendored
21
.github/pull_request_template.md
vendored
@ -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
23
.github/stale.yml
vendored
Normal 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
1
.gitignore
vendored
@ -1,4 +1,5 @@
|
||||
*.pyc
|
||||
.tox/
|
||||
build/
|
||||
dist/
|
||||
doc/build
|
||||
|
||||
1
.gitmodules
vendored
1
.gitmodules
vendored
@ -1,4 +1,3 @@
|
||||
[submodule "odpi"]
|
||||
path = odpi
|
||||
url = ../odpi.git
|
||||
branch = v3.1.x
|
||||
|
||||
16
.readthedocs.yaml
Normal file
16
.readthedocs.yaml
Normal 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
|
||||
@ -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.
|
||||
|
||||
74
README.md
74
README.md
@ -1,59 +1,33 @@
|
||||
# cx_Oracle version 7.1
|
||||
# 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).
|
||||
|
||||
@ -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
37
SECURITY.md
Normal 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
2
doc/requirements.txt
Normal file
@ -0,0 +1,2 @@
|
||||
sphinx>=4.2.0
|
||||
sphinx-rtd-theme>=0.5.2
|
||||
@ -1,8 +1,133 @@
|
||||
.. _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::
|
||||
|
||||
All of these objects are extensions to the DB API.
|
||||
|
||||
.. _queue:
|
||||
|
||||
------
|
||||
Queues
|
||||
------
|
||||
|
||||
These objects are created using the :meth:`Connection.queue()` method and are
|
||||
used to enqueue and dequeue messages.
|
||||
|
||||
.. attribute:: Queue.connection
|
||||
|
||||
This read-only attribute returns a reference to the connection object on
|
||||
which the queue was created.
|
||||
|
||||
|
||||
.. method:: Queue.deqmany(maxMessages)
|
||||
|
||||
Dequeues up to the specified number of messages from the queue and returns
|
||||
a list of these messages. Each element of the returned list is a
|
||||
:ref:`message property<msgproperties>` object.
|
||||
|
||||
.. versionchanged:: 8.2
|
||||
|
||||
For consistency and compliance with the PEP 8 naming style, the name of
|
||||
the method was changed from `deqMany()`. The old name will continue to
|
||||
work for a period of time.
|
||||
|
||||
.. method:: Queue.deqone()
|
||||
|
||||
Dequeues at most one message from the queue. If a message is dequeued, it
|
||||
will be a :ref:`message property<msgproperties>` object; otherwise, it will
|
||||
be the value None.
|
||||
|
||||
.. versionchanged:: 8.2
|
||||
|
||||
For consistency and compliance with the PEP 8 naming style, the name of
|
||||
the method was changed from `deqOne()`. The old name will continue to
|
||||
work for a period of time.
|
||||
|
||||
.. attribute:: Queue.deqoptions
|
||||
|
||||
This read-only attribute returns a reference to the :ref:`options
|
||||
<deqoptions>` that will be used when dequeuing messages from the queue.
|
||||
|
||||
.. versionchanged:: 8.2
|
||||
|
||||
For consistency and compliance with the PEP 8 naming style, the name of
|
||||
the attribute was changed from `deqOptions`. The old name will continue
|
||||
to work for a period of time.
|
||||
|
||||
.. method:: Queue.enqmany(messages)
|
||||
|
||||
Enqueues multiple messages into the queue. The messages parameter must be a
|
||||
sequence containing :ref:`message property <msgproperties>` objects which
|
||||
have all had their payload attribute set to a value that the queue
|
||||
supports.
|
||||
|
||||
Warning: calling this function in parallel on different connections
|
||||
acquired from the same pool may fail due to Oracle bug 29928074. Ensure
|
||||
that this function is not run in parallel, use standalone connections or
|
||||
connections from different pools, or make multiple calls to
|
||||
:meth:`Queue.enqone()` instead. The function :meth:`Queue.deqmany()`
|
||||
call is not affected.
|
||||
|
||||
.. versionchanged:: 8.2
|
||||
|
||||
For consistency and compliance with the PEP 8 naming style, the name of
|
||||
the method was changed from `enqMany()`. The old name will continue
|
||||
to work for a period of time.
|
||||
|
||||
|
||||
.. method:: Queue.enqone(message)
|
||||
|
||||
Enqueues a single message into the queue. The message must be a
|
||||
:ref:`message property<msgproperties>` object which has had its payload
|
||||
attribute set to a value that the queue supports.
|
||||
|
||||
.. versionchanged:: 8.2
|
||||
|
||||
For consistency and compliance with the PEP 8 naming style, the name of
|
||||
the method was changed from `enqOne()`. The old name will continue
|
||||
to work for a period of time.
|
||||
|
||||
.. attribute:: Queue.enqoptions
|
||||
|
||||
This read-only attribute returns a reference to the :ref:`options
|
||||
<enqoptions>` that will be used when enqueuing messages into the queue.
|
||||
|
||||
.. versionchanged:: 8.2
|
||||
|
||||
For consistency and compliance with the PEP 8 naming style, the name of
|
||||
the attribute was changed from `enqOptions`. The old name will continue
|
||||
to work for a period of time.
|
||||
|
||||
|
||||
.. attribute:: Queue.name
|
||||
|
||||
This read-only attribute returns the name of the queue.
|
||||
|
||||
|
||||
.. attribute:: Queue.payload_type
|
||||
|
||||
This read-only attribute returns the object type for payloads that can be
|
||||
enqueued and dequeued. If using a raw queue, this returns the value None.
|
||||
|
||||
.. versionchanged:: 8.2
|
||||
|
||||
For consistency and compliance with the PEP 8 naming style, the name of
|
||||
the attribute was changed from `payloadType`. The old name will
|
||||
continue to work for a period of time.
|
||||
|
||||
|
||||
.. _deqoptions:
|
||||
|
||||
@ -12,9 +137,9 @@ Dequeue Options
|
||||
|
||||
.. note::
|
||||
|
||||
This object is an extension to the DB API. It is returned by the
|
||||
:meth:`Connection.deqoptions()` call and is used in calls to
|
||||
:meth:`Connection.deq()`.
|
||||
These objects are used to configure how messages are dequeued from queues.
|
||||
An instance of this object is found in the attribute
|
||||
:attr:`Queue.deqOptions`.
|
||||
|
||||
|
||||
.. attribute:: DeqOptions.condition
|
||||
@ -107,9 +232,9 @@ Enqueue Options
|
||||
|
||||
.. note::
|
||||
|
||||
This object is an extension to the DB API. It is returned by the
|
||||
:meth:`Connection.enqoptions()` call and is used in calls to
|
||||
:meth:`Connection.enq()`.
|
||||
These objects are used to configure how messages are enqueued into queues.
|
||||
An instance of this object is found in the attribute
|
||||
:attr:`Queue.enqOptions`.
|
||||
|
||||
|
||||
.. attribute:: EnqOptions.deliverymode
|
||||
@ -143,9 +268,11 @@ Message Properties
|
||||
|
||||
.. note::
|
||||
|
||||
This object is an extension to the DB API. It is returned by the
|
||||
:meth:`Connection.msgproperties()` call and is used in calls to
|
||||
:meth:`Connection.deq()` and :meth:`Connection.enq()`.
|
||||
These objects are used to identify the properties of messages that are
|
||||
enqueued and dequeued in queues. They are created by the method
|
||||
:meth:`Connection.msgproperties()`. They are used by the methods
|
||||
:meth:`Queue.enqone()` and :meth:`Queue.enqmany()` and
|
||||
returned by the methods :meth:`Queue.deqone()` and :meth:`Queue.deqmany()`.
|
||||
|
||||
|
||||
.. attribute:: MessageProperties.attempts
|
||||
@ -201,8 +328,19 @@ 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
|
||||
|
||||
This attribute identifies the payload that will be enqueued or the payload
|
||||
that was dequeued when using a :ref:`queue <queue>`. When enqueuing, the
|
||||
value is checked to ensure that it conforms to the type expected by that
|
||||
queue. For RAW queues, the value can be a bytes object or a string. If the
|
||||
value is a string it will first be converted to bytes by encoding in the
|
||||
encoding identified by the attribute :attr:`Connection.encoding`.
|
||||
|
||||
|
||||
.. attribute:: MessageProperties.priority
|
||||
@ -218,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`.
|
||||
|
||||
@ -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,12 +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
|
||||
@ -82,7 +102,7 @@ Connection Object
|
||||
|
||||
.. method:: Connection.cancel()
|
||||
|
||||
Cancel a long-running transaction.
|
||||
Break a long-running transaction.
|
||||
|
||||
.. note::
|
||||
|
||||
@ -191,6 +211,11 @@ Connection Object
|
||||
|
||||
.. versionadded:: 5.3
|
||||
|
||||
.. deprecated:: 7.2
|
||||
|
||||
Use the methods :meth:`Queue.deqone()` or :meth:`Queue.deqmany()`
|
||||
instead.
|
||||
|
||||
.. note::
|
||||
|
||||
This method is an extension to the DB API definition.
|
||||
@ -203,6 +228,10 @@ Connection Object
|
||||
|
||||
.. versionadded:: 5.3
|
||||
|
||||
.. deprecated:: 7.2
|
||||
|
||||
Use the attribute :attr:`Queue.deqoptions` instead.
|
||||
|
||||
.. note::
|
||||
|
||||
This method is an extension to the DB API definition.
|
||||
@ -236,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.
|
||||
@ -251,6 +282,11 @@ Connection Object
|
||||
|
||||
.. versionadded:: 5.3
|
||||
|
||||
.. deprecated:: 7.2
|
||||
|
||||
Use the methods :meth:`Queue.enqone()` or :meth:`Queue.enqmany()`
|
||||
instead.
|
||||
|
||||
.. note::
|
||||
|
||||
This method is an extension to the DB API definition.
|
||||
@ -263,6 +299,10 @@ Connection Object
|
||||
|
||||
.. versionadded:: 5.3
|
||||
|
||||
.. deprecated:: 7.2
|
||||
|
||||
Use the attribute :attr:`Queue.enqoptions` instead.
|
||||
|
||||
.. note::
|
||||
|
||||
This method is an extension to the DB API definition.
|
||||
@ -285,14 +325,10 @@ 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.
|
||||
|
||||
SODA support in cx_Oracle is in Preview status and should not be used in
|
||||
production. It will be supported with a future version of Oracle Client
|
||||
libraries.
|
||||
|
||||
.. versionadded:: 7.0
|
||||
|
||||
.. note::
|
||||
@ -371,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.
|
||||
@ -387,13 +425,19 @@ Connection Object
|
||||
This attribute is an extension to the DB API definition.
|
||||
|
||||
|
||||
.. method:: Connection.msgproperties()
|
||||
.. method:: Connection.msgproperties(payload, correlation, delay, exceptionq, \
|
||||
expiration, priority)
|
||||
|
||||
Returns an object specifying the properties of messages used in advanced
|
||||
queuing. See :ref:`msgproperties` for more information.
|
||||
|
||||
Each of the parameters are optional. If specified, they act as a shortcut
|
||||
for setting each of the equivalently named properties.
|
||||
|
||||
.. versionadded:: 5.3
|
||||
|
||||
.. versionchanged:: 7.2 Added parameters
|
||||
|
||||
.. note::
|
||||
|
||||
This method is an extension to the DB API definition.
|
||||
@ -404,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.
|
||||
@ -419,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.
|
||||
@ -445,6 +493,31 @@ Connection Object
|
||||
This method is an extension to the DB API definition.
|
||||
|
||||
|
||||
.. method:: Connection.queue(name, payload_type=None)
|
||||
|
||||
Creates a :ref:`queue <queue>` which is used to enqueue and dequeue
|
||||
messages in Advanced Queueing.
|
||||
|
||||
The name parameter is expected to be a string identifying the queue in
|
||||
which messages are to be enqueued or dequeued.
|
||||
|
||||
The payload_type parameter, if specified, is expected to be an
|
||||
:ref:`object type <objecttype>` that identifies the type of payload the
|
||||
queue expects. If not specified, RAW data is enqueued and dequeued.
|
||||
|
||||
.. versionadded:: 7.2
|
||||
|
||||
.. versionchanged:: 8.2
|
||||
|
||||
For consistency and compliance with the PEP 8 naming style, the
|
||||
parameter `payloadType` was renamed to `payload_type`. The old name
|
||||
will continue to work as a keyword parameter for a period of time.
|
||||
|
||||
.. note::
|
||||
|
||||
This method is an extension to the DB API definition.
|
||||
|
||||
|
||||
.. method:: Connection.rollback()
|
||||
|
||||
Rollback any pending transactions.
|
||||
@ -473,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:
|
||||
|
||||
::
|
||||
|
||||
@ -500,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
|
||||
@ -546,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`.
|
||||
|
||||
@ -569,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::
|
||||
|
||||
@ -581,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.
|
||||
|
||||
|
||||
@ -610,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.
|
||||
@ -649,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).
|
||||
|
||||
@ -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.
|
||||
@ -424,8 +500,10 @@ Cursor Object
|
||||
.. attribute:: Cursor.rowcount
|
||||
|
||||
This read-only attribute specifies the number of rows that have currently
|
||||
been fetched from the cursor (for select statements) or that have been
|
||||
affected by the operation (for insert, update and delete statements).
|
||||
been fetched from the cursor (for select statements), that have been
|
||||
affected by the operation (for insert, update, delete and merge
|
||||
statements), or the number of successful executions of the statement
|
||||
(for PL/SQL statements).
|
||||
|
||||
|
||||
.. attribute:: Cursor.rowfactory
|
||||
@ -436,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.
|
||||
@ -514,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,
|
||||
@ -545,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.
|
||||
|
||||
184
doc/src/api_manual/deprecations.rst
Normal file
184
doc/src/api_manual/deprecations.rst
Normal 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.
|
||||
@ -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
@ -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
|
||||
@ -4,10 +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, \
|
||||
@ -37,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
|
||||
|
||||
@ -48,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.
|
||||
@ -66,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
|
||||
@ -96,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
|
||||
@ -120,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__
|
||||
@ -143,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
|
||||
|
||||
@ -167,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
|
||||
|
||||
@ -183,4 +307,3 @@ SessionPool Object
|
||||
:data:`cx_Oracle.SPOOL_ATTRVAL_TIMEDWAIT`.
|
||||
|
||||
.. versionadded:: 6.4
|
||||
|
||||
@ -4,16 +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.
|
||||
|
||||
SODA support in cx_Oracle is in Preview status and should not be used in
|
||||
production. It will be supported with a future version of Oracle Client
|
||||
libraries.
|
||||
.. 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:
|
||||
|
||||
@ -111,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()`.
|
||||
|
||||
@ -181,6 +240,52 @@ SODA Collection Object
|
||||
.. versionadded:: 7.0
|
||||
|
||||
|
||||
.. method:: SodaCollection.insertMany(docs)
|
||||
|
||||
Inserts a list of documents into the collection at one time. Each of the
|
||||
input documents can be a dictionary or list or an existing :ref:`SODA
|
||||
document object <sodadoc>`.
|
||||
|
||||
.. note::
|
||||
|
||||
This method requires Oracle Client 18.5 and higher and is available
|
||||
only as a preview.
|
||||
|
||||
.. versionadded:: 7.2
|
||||
|
||||
|
||||
.. method:: SodaCollection.insertManyAndGet(docs, hint=None)
|
||||
|
||||
Similarly to :meth:`~SodaCollection.insertMany()` this method inserts a
|
||||
list of documents into the collection at one time. The only difference is
|
||||
that it returns a list of :ref:`SODA Document objects <sodadoc>`. Note that
|
||||
for performance reasons the returned documents do not contain the content.
|
||||
|
||||
The hint parameter, if specified, supplies a hint to the database when
|
||||
processing the SODA operation. This is expected to be a string in the same
|
||||
format as a SQL hint but without any comment characters, for example
|
||||
``hint="MONITOR"``. Pass only the hint ``"MONITOR"`` (turn on monitoring)
|
||||
or ``"NO_MONITOR"`` (turn off monitoring). See the Oracle Database SQL
|
||||
Tuning Guide documentation `MONITOR and NO_MONITOR Hints
|
||||
<https://www.oracle.com/pls/topic/lookup?
|
||||
ctx=dblatest&id=GUID-19E0F73C-A959-41E4-A168-91E436DEE1F1>`__
|
||||
and `Monitoring Database Operations
|
||||
<https://www.oracle.com/pls/topic/lookup?
|
||||
ctx=dblatest&id=GUID-C941CE9D-97E1-42F8-91ED-4949B2B710BF>`__
|
||||
for more information.
|
||||
|
||||
.. note::
|
||||
|
||||
This method requires Oracle Client 18.5 and higher.
|
||||
|
||||
.. versionadded:: 7.2
|
||||
|
||||
.. versionchanged:: 8.2
|
||||
|
||||
The parameter `hint` was added. Use of the hint parameter requires
|
||||
Oracle Client 21.3 or higher (or Oracle Client 19 from 19.11).
|
||||
|
||||
|
||||
.. method:: SodaCollection.insertOne(doc)
|
||||
|
||||
Inserts a given document into the collection. The input document can be a
|
||||
@ -189,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>`__
|
||||
@ -217,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:
|
||||
|
||||
--------------------
|
||||
@ -356,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
|
||||
@ -398,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.
|
||||
@ -493,4 +704,3 @@ SODA Operation Object
|
||||
criteria can be specified by chaining methods together.
|
||||
|
||||
.. versionadded:: 7.0
|
||||
|
||||
@ -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.
|
||||
@ -23,14 +30,13 @@ Subscription Object
|
||||
|
||||
.. attribute:: Subscription.id
|
||||
|
||||
This read-only attribute returns the value 0.
|
||||
|
||||
.. deprecated:: 6.0
|
||||
This attribute was never intended to be exposed and will be removed
|
||||
in cx_Oracle 7.
|
||||
This read-only attribute returns the value of ``REGID`` found in the
|
||||
database view ``USER_CHANGE_NOTIFICATION_REGS`` or the value of ``REG_ID``
|
||||
found in the database view ``USER_SUBSCR_REGISTRATIONS``. For AQ
|
||||
subscriptions, the value is 0.
|
||||
|
||||
|
||||
.. attribute:: Subscription.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,
|
||||
@ -38,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
|
||||
|
||||
@ -99,7 +111,7 @@ Subscription Object
|
||||
.. _msgobjects:
|
||||
|
||||
Message Objects
|
||||
===============
|
||||
---------------
|
||||
|
||||
.. note::
|
||||
|
||||
@ -107,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
|
||||
@ -116,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
|
||||
|
||||
@ -131,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
|
||||
@ -139,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
|
||||
|
||||
@ -178,7 +202,7 @@ Message Objects
|
||||
|
||||
|
||||
Message Table Objects
|
||||
=====================
|
||||
---------------------
|
||||
|
||||
.. note::
|
||||
|
||||
@ -207,7 +231,7 @@ Message Table Objects
|
||||
|
||||
|
||||
Message Row Objects
|
||||
===================
|
||||
-------------------
|
||||
|
||||
.. note::
|
||||
|
||||
@ -228,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.
|
||||
|
||||
|
||||
@ -256,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.
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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.1'
|
||||
version = '8.3'
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = '7.1.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
|
||||
# ------------------------
|
||||
|
||||
BIN
doc/src/images/cx_Oracle_arch.png
Normal file
BIN
doc/src/images/cx_Oracle_arch.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 88 KiB |
@ -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 11.2, 12.1, 12.2 and 18.3 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`
|
||||
|
||||
|
||||
@ -1,667 +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 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 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 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 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 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 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-18.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_18_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_18_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 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-instantclient18.3-basic-18.3.0.0.0-1.x86_64.rpm
|
||||
|
||||
Yum will automatically install required dependencies, such as ``libaio``.
|
||||
|
||||
3. 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, set the environment variable ``LD_LIBRARY_PATH`` 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 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 single directory that is accessible to your
|
||||
application, for example ``C:\oracle\instantclient_18_3``.
|
||||
|
||||
3. Set the environment variable ``PATH`` to include the path that you
|
||||
created in step 2. For example, on Windows 7, update ``PATH`` in
|
||||
Control Panel -> System -> Advanced System Settings -> Advanced ->
|
||||
Environment Variables -> System Variables -> PATH. Alternatively
|
||||
use ``SET`` to change your ``PATH`` in each command prompt window
|
||||
before you run python.
|
||||
|
||||
If you have other Oracle software installed, then when you use
|
||||
Python you will need to make sure that the Instant Client
|
||||
directory, e.g. ``C:\oracle\instantclient_18_3``, occurs in
|
||||
``PATH`` before any other Oracle directories.
|
||||
|
||||
Restart any open command prompt windows.
|
||||
|
||||
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 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-12.2.0.1.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_12_2/libclntsh.dylib ~/lib/
|
||||
|
||||
Alternatively, copy the required OCI libraries. For example::
|
||||
|
||||
mkdir ~/lib
|
||||
cp /opt/oracle/instantclient_12_2/{libclntsh.dylib.12.1,libclntshcore.dylib.12.1,libons.dylib,libnnz12.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. 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.
|
||||
|
||||
|
||||
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.
|
||||
@ -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.
|
||||
|
||||
|
||||
@ -1,12 +1,449 @@
|
||||
: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.
|
||||
|
||||
|
||||
Version 7.2 (July 2019)
|
||||
-----------------------
|
||||
|
||||
#) Updated embedded ODPI-C to `version 3.2
|
||||
<https://oracle.github.io/odpi/doc/releasenotes.html#
|
||||
version-3-2-july-1-2019>`__.
|
||||
#) Improved AQ support
|
||||
|
||||
- added support for enqueue and dequeue of RAW payloads
|
||||
- added support for bulk enqueue and dequeue of messages
|
||||
- added new method :meth:`Connection.queue()` which creates a new
|
||||
:ref:`queue object <queue>` in order to simplify AQ usage
|
||||
- enhanced method :meth:`Connection.msgproperties()` to allow the writable
|
||||
properties of the newly created object to be initialized.
|
||||
- the original methods for enqueueing and dequeuing (Connection.deq(),
|
||||
Connection.deqoptions(), Connection.enq() and Connection.enqoptions())
|
||||
are now deprecated and will be removed in a future version.
|
||||
|
||||
#) Removed preview status from existing SODA functionality. See
|
||||
`this tracking issue
|
||||
<https://github.com/oracle/python-cx_Oracle/issues/309>`__ for known issues
|
||||
with SODA.
|
||||
#) Added support for a preview of SODA bulk insert, available in Oracle Client
|
||||
18.5 and higher.
|
||||
#) Added support for setting LOB object attributes, as requested
|
||||
(`issue 299 <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 using the LOB prefetch length indicator in order to
|
||||
reduce the number of round trips when fetching LOBs and then subsequently
|
||||
calling :meth:`LOB.size()`, :meth:`LOB.getchunksize()` or
|
||||
:meth:`LOB.read()`. This is always enabled.
|
||||
#) Added support for types BINARY_INTEGER, PLS_INTEGER, ROWID, LONG and LONG
|
||||
RAW when used in PL/SQL.
|
||||
#) Eliminated deprecation of attribute :attr:`Subscription.id`. It is now
|
||||
populated with the value of ``REGID`` found in the database view
|
||||
``USER_CHANGE_NOTIFICATION_REGS`` or the value of ``REG_ID`` found in the
|
||||
database view ``USER_SUBSCR_REGISTRATIONS``. For AQ subscriptions, the
|
||||
value is 0.
|
||||
#) Enabled PY_SSIZE_T_CLEAN, as required by Python 3.8
|
||||
(`issue 317 <https://github.com/oracle/python-cx_Oracle/issues/317>`__).
|
||||
#) Eliminated memory leak when fetching objects that are atomically null
|
||||
(`issue 298 <https://github.com/oracle/python-cx_Oracle/issues/298>`__).
|
||||
#) Eliminated bug when processing the string representation of numbers like
|
||||
1e-08 and 1e-09 (`issue 300
|
||||
<https://github.com/oracle/python-cx_Oracle/issues/300>`__).
|
||||
#) Improved error message when the parent cursor is closed before a fetch is
|
||||
attempted from an implicit result cursor.
|
||||
#) Improved test suite and samples.
|
||||
#) Improved documentation.
|
||||
|
||||
|
||||
Version 7.1.3 (April 2019)
|
||||
--------------------------
|
||||
|
||||
#) Updated to `ODPI-C 3.1.4
|
||||
<https://oracle.github.io/odpi/doc/releasenotes.html#
|
||||
version-3-1-4-april-24-2019>`__.
|
||||
#) Added support for getting the row count for PL/SQL statements
|
||||
(`issue 285 <https://github.com/oracle/python-cx_Oracle/issues/285>`__).
|
||||
#) Corrected parsing of connect string so that the last @ symbol is searched
|
||||
for instead of the first @ symbol; otherwise, passwords containing an @
|
||||
symbol will result in the incorrect DSN being extracted
|
||||
(`issue 290 <https://github.com/oracle/python-cx_Oracle/issues/290>`__).
|
||||
#) Adjusted return value of cursor.callproc() to follow documentation (only
|
||||
positional arguments are returned since the order of keyword parameters
|
||||
cannot be guaranteed in any case)
|
||||
(`PR 287 <https://github.com/oracle/python-cx_Oracle/pull/287>`__).
|
||||
#) Corrected code getting sample and test parameters by user input when using
|
||||
Python 2.7.
|
||||
|
||||
|
||||
Version 7.1.2 (March 2019)
|
||||
--------------------------
|
||||
|
||||
#) Updated to `ODPI-C 3.1.3
|
||||
<https://oracle.github.io/odpi/doc/releasenotes.html#
|
||||
version-3-1-3-march-12-2019>`__.
|
||||
#) Ensured that the strings "-0" and "-0.0" are correctly handled as zero
|
||||
values
|
||||
(`issue 274 <https://github.com/oracle/python-cx_Oracle/issues/274>`__).
|
||||
#) Eliminated error when startup and shutdown events are generated
|
||||
(`ODPI-C issue 102 <https://github.com/oracle/odpi/issues/102>`__).
|
||||
#) Enabled the types specified in :meth:`Cursor.setinputsizes()` and
|
||||
:meth:`Cursor.callfunc()` to be an object type in addition to a Python
|
||||
type, just like in :meth:`Cursor.var()`.
|
||||
#) Reverted changes to return decimal numbers when the numeric precision was
|
||||
too great to be returned accurately as a floating point number. This change
|
||||
had too great an impact on existing functionality and an output type
|
||||
handler can be used to return decimal numbers where that is desirable
|
||||
(`issue 279 <https://github.com/oracle/python-cx_Oracle/issues/279>`__).
|
||||
#) Eliminated discrepancies in character sets between an external connection
|
||||
handle and the newly created connection handle that references the external
|
||||
connection handle
|
||||
(`issue 273 <https://github.com/oracle/python-cx_Oracle/issues/273>`__).
|
||||
#) Eliminated memory leak when receiving messages received from subscriptions.
|
||||
#) Improved test suite and documentation.
|
||||
|
||||
|
||||
Version 7.1.1 (February 2019)
|
||||
-----------------------------
|
||||
|
||||
#) Updated to `ODPI-C 3.1.2
|
||||
<https://oracle.github.io/odpi/doc/releasenotes.html#
|
||||
version-3-1-2-february-19-2019>`__.
|
||||
#) Corrected code for freeing CQN message objects when multiple queries are
|
||||
registered
|
||||
(`ODPI-C issue 96 <https://github.com/oracle/odpi/issues/96>`__).
|
||||
#) Improved error messages and installation documentation.
|
||||
|
||||
.. _releasenotes70:
|
||||
|
||||
Version 7.1 (February 2019)
|
||||
---------------------------
|
||||
@ -102,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()`.
|
||||
@ -136,12 +573,6 @@ Version 7.0 (September 2018)
|
||||
#) Improved documentation.
|
||||
|
||||
|
||||
|
||||
6.x releases
|
||||
############
|
||||
|
||||
.. _releasenotes60:
|
||||
|
||||
Version 6.4.1 (July 2018)
|
||||
-------------------------
|
||||
|
||||
@ -203,7 +634,8 @@ Version 6.4 (July 2018)
|
||||
|
||||
#) Added support for using the cursor as a context manager
|
||||
(`issue 190 <https://github.com/oracle/python-cx_Oracle/issues/190>`__).
|
||||
#) Added support for specifying the "errors" parameter to the decode() that
|
||||
#) Added parameter "encodingErrors" to function :meth:`Cursor.var()` in order
|
||||
to add support for specifying the "errors" parameter to the decode() that
|
||||
takes place internally when fetching strings from the database
|
||||
(`issue 162 <https://github.com/oracle/python-cx_Oracle/issues/162>`__).
|
||||
#) Added support for specifying an integer for the parameters argument to
|
||||
@ -237,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
|
||||
@ -436,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
|
||||
@ -508,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>`__.
|
||||
|
||||
@ -655,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
|
||||
@ -688,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)
|
||||
@ -1020,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)
|
||||
----------------------------
|
||||
|
||||
@ -1140,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.
|
||||
@ -1287,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.
|
||||
|
||||
|
||||
@ -1655,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
218
doc/src/user_guide/aq.rst
Normal 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.
|
||||
301
doc/src/user_guide/batch_statement.rst
Normal file
301
doc/src/user_guide/batch_statement.rst
Normal 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
812
doc/src/user_guide/bind.rst
Normal 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``.
|
||||
1599
doc/src/user_guide/connection_handling.rst
Normal file
1599
doc/src/user_guide/connection_handling.rst
Normal file
File diff suppressed because it is too large
Load Diff
165
doc/src/user_guide/cqn.rst
Normal file
165
doc/src/user_guide/cqn.rst
Normal 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.
|
||||
47
doc/src/user_guide/exception_handling.rst
Normal file
47
doc/src/user_guide/exception_handling.rst
Normal 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")
|
||||
164
doc/src/user_guide/globalization.rst
Normal file
164
doc/src/user_guide/globalization.rst
Normal 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 Oracle’s National Language Support (NLS) to assist in
|
||||
globalizing applications. As well as character set support, there are many
|
||||
other features that will be useful in applications. See the
|
||||
`Database Globalization Support Guide
|
||||
<https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=NLSPG>`__.
|
||||
|
||||
|
||||
Setting the Client Character Set
|
||||
================================
|
||||
|
||||
In cx_Oracle 8 the default encoding used for all character data changed to
|
||||
"UTF-8". This universal encoding is suitable for most applications. If you
|
||||
have a special need, you can pass the ``encoding`` and ``nencoding`` parameters
|
||||
to the :meth:`cx_Oracle.connect` and :meth:`cx_Oracle.SessionPool` methods to
|
||||
specify different Oracle Client character sets. For example:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import cx_Oracle
|
||||
connection = cx_Oracle.connect(dsn=connect_string, encoding="US-ASCII",
|
||||
nencoding="UTF-8")
|
||||
|
||||
.. note::
|
||||
|
||||
In a future release of cx_Oracle, only UTF-8 will be supported.
|
||||
|
||||
The ``encoding`` parameter affects character data such as VARCHAR2 and CLOB
|
||||
columns. The ``nencoding`` parameter affects "National Character" data such as
|
||||
NVARCHAR2 and NCLOB. If you are not using national character types, then you
|
||||
can omit ``nencoding``. Both the ``encoding`` and ``nencoding`` parameters are
|
||||
expected to be one of the `Python standard encodings
|
||||
<https://docs.python.org/3/library/codecs.html#standard-encodings>`__ such as
|
||||
``UTF-8``. Do not accidentally use ``UTF8``, which Oracle uses to specify the
|
||||
older Unicode 3.0 Universal character set, ``CESU-8``. Note that Oracle does
|
||||
not recognize all of the encodings that Python recognizes. You can see which
|
||||
encodings are usable in cx_Oracle by issuing this query:
|
||||
|
||||
.. code-block:: sql
|
||||
|
||||
select distinct utl_i18n.map_charset(value)
|
||||
from v$nls_valid_values
|
||||
where parameter = 'CHARACTERSET'
|
||||
and utl_i18n.map_charset(value) is not null
|
||||
order by 1
|
||||
|
||||
.. note::
|
||||
|
||||
From cx_Oracle 8, it is no longer possible to change the character set
|
||||
using the ``NLS_LANG`` environment variable. The character set component
|
||||
of that variable is ignored. The language and territory components of
|
||||
``NLS_LANG`` are still respected by the Oracle Client libraries.
|
||||
|
||||
Character Set Example
|
||||
---------------------
|
||||
|
||||
The script below tries to display data containing a Euro symbol from the
|
||||
database.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
connection = cx_Oracle.connect(user=user, password=password,
|
||||
dsn="dbhost.example.com/orclpdb1",
|
||||
encoding="US-ASCII")
|
||||
cursor = connection.cursor()
|
||||
for row in cursor.execute("select nvarchar2_column from nchar_test"):
|
||||
print(row)
|
||||
|
||||
Because the '€' symbol is not supported by the ``US-ASCII`` character set, all
|
||||
'€' characters are replaced by '¿' in the cx_Oracle output::
|
||||
|
||||
('¿',)
|
||||
|
||||
When the ``encoding`` parameter is removed (or set to "UTF-8") during
|
||||
connection:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
connection = cx_Oracle.connect(user=user, password=password,
|
||||
dsn="dbhost.example.com/orclpdb1")
|
||||
|
||||
Then the output displays the Euro symbol as desired::
|
||||
|
||||
('€',)
|
||||
|
||||
.. _findingcharset:
|
||||
|
||||
Finding the Database and Client Character Set
|
||||
---------------------------------------------
|
||||
|
||||
To find the database character set, execute the query:
|
||||
|
||||
.. code-block:: sql
|
||||
|
||||
SELECT value AS db_charset
|
||||
FROM nls_database_parameters
|
||||
WHERE parameter = 'NLS_CHARACTERSET';
|
||||
|
||||
To find the database 'national character set' used for NCHAR and related types,
|
||||
execute the query:
|
||||
|
||||
.. code-block:: sql
|
||||
|
||||
SELECT value AS db_ncharset
|
||||
FROM nls_database_parameters
|
||||
WHERE parameter = 'NLS_NCHAR_CHARACTERSET';
|
||||
|
||||
To find the current "client" character set used by cx_Oracle, execute the
|
||||
query:
|
||||
|
||||
.. code-block:: sql
|
||||
|
||||
SELECT DISTINCT client_charset AS client_charset
|
||||
FROM v$session_connect_info
|
||||
WHERE sid = SYS_CONTEXT('USERENV', 'SID');
|
||||
|
||||
If these character sets do not match, characters transferred over Oracle Net
|
||||
will be mapped from one character set to another. This may impact performance
|
||||
and may result in invalid data.
|
||||
|
||||
Setting the Oracle Client Locale
|
||||
================================
|
||||
|
||||
You can use the ``NLS_LANG`` environment variable to set the language and
|
||||
territory used by the Oracle Client libraries. For example, on Linux you could
|
||||
set::
|
||||
|
||||
export NLS_LANG=JAPANESE_JAPAN
|
||||
|
||||
The language ("JAPANESE" in this example) specifies conventions such as the
|
||||
language used for Oracle Database messages, sorting, day names, and month
|
||||
names. The territory ("JAPAN") specifies conventions such as the default date,
|
||||
monetary, and numeric formats. If the language is not specified, then the value
|
||||
defaults to AMERICAN. If the territory is not specified, then the value is
|
||||
derived from the language value. See `Choosing a Locale with the NLS_LANG
|
||||
Environment Variable
|
||||
<https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-86A29834-AE29-4BA5-8A78-E19C168B690A>`__
|
||||
|
||||
If the ``NLS_LANG`` environment variable is set in the application with
|
||||
``os.environ['NLS_LANG']``, it must be set before any connection pool is
|
||||
created, or before any standalone connections are created.
|
||||
|
||||
Other Oracle globalization variables, such as ``NLS_DATE_FORMAT`` can also be
|
||||
set to change the behavior of cx_Oracle, see `Setting NLS Parameters
|
||||
<https://www.oracle.com/pls/topic/lookup?ctx=dblatest&
|
||||
id=GUID-6475CA50-6476-4559-AD87-35D431276B20>`__.
|
||||
223
doc/src/user_guide/ha.rst
Normal file
223
doc/src/user_guide/ha.rst
Normal 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.
|
||||
415
doc/src/user_guide/initialization.rst
Normal file
415
doc/src/user_guide/initialization.rst
Normal 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.
|
||||
936
doc/src/user_guide/installation.rst
Normal file
936
doc/src/user_guide/installation.rst
Normal 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.
|
||||
145
doc/src/user_guide/introduction.rst
Normal file
145
doc/src/user_guide/introduction.rst
Normal 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). Oracle’s standard
|
||||
client-server version interoperability allows connection to both older and
|
||||
newer databases from different Client library versions, see :ref:`cx_Oracle
|
||||
Installation <installation>`.
|
||||
|
||||
Some behaviors of the Oracle Client libraries can optionally be configured with
|
||||
an ``oraaccess.xml`` file, for example to enable auto-tuning of a statement
|
||||
cache. See :ref:`optclientfiles`.
|
||||
|
||||
The Oracle Net layer can optionally be configured with files such as
|
||||
``tnsnames.ora`` and ``sqlnet.ora``, for example to enable :ref:`network
|
||||
encryption <netencrypt>`. See :ref:`optnetfiles`.
|
||||
|
||||
Oracle environment variables that are set before cx_Oracle first creates a
|
||||
database connection will affect cx_Oracle behavior. Optional variables include
|
||||
NLS_LANG, NLS_DATE_FORMAT and TNS_ADMIN. See :ref:`envset`.
|
||||
|
||||
Features
|
||||
--------
|
||||
|
||||
The cx_Oracle feature highlights are:
|
||||
|
||||
* Easy installation from PyPI
|
||||
* Support for multiple Oracle Client and Database versions
|
||||
* Execution of SQL and PL/SQL statements
|
||||
* Extensive Oracle data type support, including large objects (CLOB and
|
||||
BLOB) and binding of SQL objects
|
||||
* Connection management, including connection pooling
|
||||
* Oracle Database High Availability features
|
||||
* Full use of Oracle Network Service infrastructure, including encrypted
|
||||
network traffic and security features
|
||||
|
||||
A complete list of supported features can be seen `here
|
||||
<https://oracle.github.io/python-cx_Oracle/index.html#features>`_.
|
||||
|
||||
Getting Started
|
||||
---------------
|
||||
|
||||
Install cx_Oracle using the :ref:`installation <installation>` steps.
|
||||
|
||||
Create a script ``query.py`` as shown below:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# query.py
|
||||
|
||||
import cx_Oracle
|
||||
|
||||
# Establish the database connection
|
||||
connection = cx_Oracle.connect(user="hr", password=userpwd,
|
||||
dsn="dbhost.example.com/orclpdb1")
|
||||
|
||||
# Obtain a cursor
|
||||
cursor = connection.cursor()
|
||||
|
||||
# Data for binding
|
||||
manager_id = 145
|
||||
first_name = "Peter"
|
||||
|
||||
# Execute the query
|
||||
sql = """SELECT first_name, last_name
|
||||
FROM employees
|
||||
WHERE manager_id = :mid AND first_name = :fn"""
|
||||
cursor.execute(sql, mid=manager_id, fn=first_name)
|
||||
|
||||
# Loop over the result set
|
||||
for row in cursor:
|
||||
print(row)
|
||||
|
||||
This uses Oracle's `sample HR schema
|
||||
<https://github.com/oracle/db-sample-schemas>`__.
|
||||
|
||||
Simple :ref:`connection <connhandling>` to the database requires a username,
|
||||
password and connection string. Locate your Oracle Database `user name and
|
||||
password <https://www.youtube.com/watch?v=WDJacg0NuLo>`_ and the database
|
||||
:ref:`connection string <connstr>`, and use them in ``query.py``. For
|
||||
cx_Oracle, the connection string is commonly of the format
|
||||
``hostname/servicename``, using the host name where the database is running and
|
||||
the Oracle Database service name of the database instance.
|
||||
|
||||
The :ref:`cursor <cursorobj>` is the object that allows statements to be
|
||||
executed and results (if any) fetched.
|
||||
|
||||
The data values in ``managerId`` and ``firstName`` are 'bound' to the statement
|
||||
placeholder 'bind variables' ``:mid`` and ``:fn`` when the statement is
|
||||
executed. This separates the statement text from the data, which helps avoid
|
||||
SQL Injection security risks. :ref:`Binding <bind>` is also important for
|
||||
performance and scalability.
|
||||
|
||||
The cursor allows rows to be iterated over and displayed.
|
||||
|
||||
Run the script::
|
||||
|
||||
python query.py
|
||||
|
||||
The output is::
|
||||
|
||||
('Peter', 'Hall')
|
||||
('Peter', 'Tucker')
|
||||
|
||||
Examples and Tutorials
|
||||
----------------------
|
||||
|
||||
Runnable examples are in the `GitHub samples directory
|
||||
<https://github.com/oracle/python-cx_Oracle/tree/main/samples>`__. A `Python
|
||||
cx_Oracle tutorial
|
||||
<https://oracle.github.io/python-cx_Oracle/samples/tutorial/Python-and-Oracle-Database-Scripting-for-the-Future.html>`__
|
||||
is also available.
|
||||
317
doc/src/user_guide/json_data_type.rst
Normal file
317
doc/src/user_guide/json_data_type.rst
Normal 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"}',)
|
||||
207
doc/src/user_guide/lob_data.rst
Normal file
207
doc/src/user_guide/lob_data.rst
Normal 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.
|
||||
388
doc/src/user_guide/plsql_execution.rst
Normal file
388
doc/src/user_guide/plsql_execution.rst
Normal 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
216
doc/src/user_guide/soda.rst
Normal 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.
|
||||
812
doc/src/user_guide/sql_execution.rst
Normal file
812
doc/src/user_guide/sql_execution.rst
Normal 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])
|
||||
69
doc/src/user_guide/startup.rst
Normal file
69
doc/src/user_guide/startup.rst
Normal 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)
|
||||
175
doc/src/user_guide/tracing_sql.rst
Normal file
175
doc/src/user_guide/tracing_sql.rst
Normal 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``.
|
||||
406
doc/src/user_guide/tuning.rst
Normal file
406
doc/src/user_guide/tuning.rst
Normal 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
|
||||
84
doc/src/user_guide/txn_management.rst
Normal file
84
doc/src/user_guide/txn_management.rst
Normal 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.
|
||||
75
doc/src/user_guide/xml_data_type.rst
Normal file
75
doc/src/user_guide/xml_data_type.rst
Normal 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.
|
||||
@ -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
2
odpi
@ -1 +1 @@
|
||||
Subproject commit c9ad23550eda45b8ac6a6eeec0aadb749b1703f5
|
||||
Subproject commit f73a7c13d643b3fe252614bafc930afbd8e287dd
|
||||
3
pyproject.toml
Normal file
3
pyproject.toml
Normal file
@ -0,0 +1,3 @@
|
||||
[build-system]
|
||||
requires = ["setuptools >= 40.6.0", "wheel"]
|
||||
build-backend = "setuptools.build_meta"
|
||||
@ -1,64 +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.
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
# AdvancedQueuing.py
|
||||
# This script demonstrates how to use advanced queuing using cx_Oracle. It
|
||||
# makes use of a simple type and queue created in the sample setup.
|
||||
#
|
||||
# This script requires cx_Oracle 5.3 and higher.
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
BOOK_TYPE_NAME = "UDT_BOOK"
|
||||
QUEUE_NAME = "BOOKS"
|
||||
QUEUE_TABLE_NAME = "BOOK_QUEUE"
|
||||
|
||||
import cx_Oracle
|
||||
import SampleEnv
|
||||
import decimal
|
||||
|
||||
# connect to database
|
||||
connection = cx_Oracle.connect(SampleEnv.GetMainConnectString())
|
||||
cursor = connection.cursor()
|
||||
|
||||
# dequeue all existing messages to ensure the queue is empty, just so that
|
||||
# the results are consistent
|
||||
booksType = connection.gettype(BOOK_TYPE_NAME)
|
||||
book = booksType.newobject()
|
||||
options = connection.deqoptions()
|
||||
options.wait = cx_Oracle.DEQ_NO_WAIT
|
||||
messageProperties = connection.msgproperties()
|
||||
while connection.deq(QUEUE_NAME, options, messageProperties, book):
|
||||
pass
|
||||
|
||||
# enqueue a few messages
|
||||
book1 = booksType.newobject()
|
||||
book1.TITLE = "The Fellowship of the Ring"
|
||||
book1.AUTHORS = "Tolkien, J.R.R."
|
||||
book1.PRICE = decimal.Decimal("10.99")
|
||||
book2 = booksType.newobject()
|
||||
book2.TITLE = "Harry Potter and the Philosopher's Stone"
|
||||
book2.AUTHORS = "Rowling, J.K."
|
||||
book2.PRICE = decimal.Decimal("7.99")
|
||||
options = connection.enqoptions()
|
||||
for book in (book1, book2):
|
||||
print("Enqueuing book", book.TITLE)
|
||||
connection.enq(QUEUE_NAME, options, messageProperties, book)
|
||||
connection.commit()
|
||||
|
||||
# dequeue the messages
|
||||
options = connection.deqoptions()
|
||||
options.navigation = cx_Oracle.DEQ_FIRST_MSG
|
||||
options.wait = cx_Oracle.DEQ_NO_WAIT
|
||||
while connection.deq(QUEUE_NAME, options, messageProperties, book):
|
||||
print("Dequeued book", book.TITLE)
|
||||
connection.commit()
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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.")
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -1,141 +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
|
||||
|
||||
# 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
|
||||
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))
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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.")
|
||||
|
||||
@ -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!")
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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.")
|
||||
@ -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
75
samples/bind_insert.py
Normal 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)
|
||||
@ -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()
|
||||
|
||||
75
samples/bulk_aq.py
Normal file
75
samples/bulk_aq.py
Normal file
@ -0,0 +1,75 @@
|
||||
#------------------------------------------------------------------------------
|
||||
# Copyright (c) 2019, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
#
|
||||
# Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved.
|
||||
#
|
||||
# Portions Copyright 2001-2007, Computronix (Canada) Ltd., Edmonton, Alberta,
|
||||
# Canada. All rights reserved.
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
# bulk_aq.py
|
||||
# This script demonstrates how to use bulk enqueuing and dequeuing of
|
||||
# messages with advanced queuing. It makes use of a RAW queue created in the
|
||||
# sample setup.
|
||||
#
|
||||
# This script requires cx_Oracle 8.2 and higher.
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
import cx_Oracle as oracledb
|
||||
import sample_env
|
||||
|
||||
QUEUE_NAME = "DEMO_RAW_QUEUE"
|
||||
PAYLOAD_DATA = [
|
||||
"The first message",
|
||||
"The second message",
|
||||
"The third message",
|
||||
"The fourth message",
|
||||
"The fifth message",
|
||||
"The sixth message",
|
||||
"The seventh message",
|
||||
"The eighth message",
|
||||
"The ninth message",
|
||||
"The tenth message",
|
||||
"The eleventh message",
|
||||
"The twelfth and final message"
|
||||
]
|
||||
|
||||
# connect to database
|
||||
connection = oracledb.connect(sample_env.get_main_connect_string())
|
||||
cursor = connection.cursor()
|
||||
|
||||
# create queue
|
||||
queue = connection.queue(QUEUE_NAME)
|
||||
queue.deqoptions.wait = oracledb.DEQ_NO_WAIT
|
||||
queue.deqoptions.navigation = oracledb.DEQ_FIRST_MSG
|
||||
|
||||
# dequeue all existing messages to ensure the queue is empty, just so that
|
||||
# the results are consistent
|
||||
while queue.deqone():
|
||||
pass
|
||||
|
||||
# enqueue a few messages
|
||||
print("Enqueuing messages...")
|
||||
batch_size = 6
|
||||
data_to_enqueue = PAYLOAD_DATA
|
||||
while data_to_enqueue:
|
||||
batch_data = data_to_enqueue[:batch_size]
|
||||
data_to_enqueue = data_to_enqueue[batch_size:]
|
||||
messages = [connection.msgproperties(payload=d) for d in batch_data]
|
||||
for data in batch_data:
|
||||
print(data)
|
||||
queue.enqmany(messages)
|
||||
connection.commit()
|
||||
|
||||
# dequeue the messages
|
||||
print("\nDequeuing messages...")
|
||||
batch_size = 8
|
||||
while True:
|
||||
messages = queue.deqmany(batch_size)
|
||||
if not messages:
|
||||
break
|
||||
for props in messages:
|
||||
print(props.payload.decode())
|
||||
connection.commit()
|
||||
print("\nDone.")
|
||||
41
samples/call_timeout.py
Normal file
41
samples/call_timeout.py
Normal file
@ -0,0 +1,41 @@
|
||||
#------------------------------------------------------------------------------
|
||||
# Copyright (c) 2019, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
# call_timeout.py
|
||||
#
|
||||
# Demonstrate the use of the Oracle Client 18c feature that enables round trips
|
||||
# to the database to time out if a specified amount of time (in milliseconds)
|
||||
# has passed without a response from the database.
|
||||
#
|
||||
# This script requires cx_Oracle 7.0 and higher and Oracle Client 18.1 and
|
||||
# higher.
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
import cx_Oracle as oracledb
|
||||
import sample_env
|
||||
|
||||
connection = oracledb.connect(sample_env.get_main_connect_string())
|
||||
connection.call_timeout = 2000
|
||||
print("Call timeout set at", connection.call_timeout, "milliseconds...")
|
||||
|
||||
cursor = connection.cursor()
|
||||
cursor.execute("select sysdate from dual")
|
||||
today, = cursor.fetchone()
|
||||
print("Fetch of current date before timeout:", today)
|
||||
|
||||
# dbms_session.sleep() replaces dbms_lock.sleep() from Oracle Database 18c
|
||||
sleep_proc_name = "dbms_session.sleep" \
|
||||
if int(connection.version.split(".")[0]) >= 18 \
|
||||
else "dbms_lock.sleep"
|
||||
|
||||
print("Sleeping...should time out...")
|
||||
try:
|
||||
cursor.callproc(sleep_proc_name, (3,))
|
||||
except oracledb.DatabaseError as e:
|
||||
print("ERROR:", e)
|
||||
|
||||
cursor.execute("select sysdate from dual")
|
||||
today, = cursor.fetchone()
|
||||
print("Fetch of current date after timeout:", today)
|
||||
78
samples/connection_pool.py
Normal file
78
samples/connection_pool.py
Normal 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!")
|
||||
@ -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
74
samples/cqn2.py
Normal 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)
|
||||
@ -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,20 +45,21 @@ 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)
|
||||
print("--> Callback:", sub.callback)
|
||||
print("--> Namespace:", sub.namespace)
|
||||
print("--> Protocol:", sub.protocol)
|
||||
print("--> Timeout:", sub.timeout)
|
||||
print("--> Operations:", sub.operations)
|
||||
print("--> Rowids?:", bool(sub.qos & 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)
|
||||
|
||||
@ -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)
|
||||
@ -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
44
samples/dbms_output.py
Normal 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
|
||||
@ -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]))
|
||||
@ -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
24
samples/drop_samples.py
Normal 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.")
|
||||
@ -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))
|
||||
@ -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()
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -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
89
samples/json_blob.py
Normal 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
94
samples/json_direct.py
Normal 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
56
samples/last_rowid.py
Normal 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()
|
||||
67
samples/multi_consumer_aq.py
Normal file
67
samples/multi_consumer_aq.py
Normal 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.")
|
||||
66
samples/object_aq.py
Normal file
66
samples/object_aq.py
Normal file
@ -0,0 +1,66 @@
|
||||
#------------------------------------------------------------------------------
|
||||
# Copyright (c) 2016, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
#
|
||||
# Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved.
|
||||
#
|
||||
# Portions Copyright 2001-2007, Computronix (Canada) Ltd., Edmonton, Alberta,
|
||||
# Canada. All rights reserved.
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
# object_aq.py
|
||||
# This script demonstrates how to use advanced queuing with objects. It makes
|
||||
# use of a simple type and queue created in the sample setup.
|
||||
#
|
||||
# This script requires cx_Oracle 8.2 and higher.
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
import decimal
|
||||
|
||||
import cx_Oracle as oracledb
|
||||
import sample_env
|
||||
|
||||
BOOK_TYPE_NAME = "UDT_BOOK"
|
||||
QUEUE_NAME = "DEMO_BOOK_QUEUE"
|
||||
BOOK_DATA = [
|
||||
("The Fellowship of the Ring", "Tolkien, J.R.R.",
|
||||
decimal.Decimal("10.99")),
|
||||
("Harry Potter and the Philosopher's Stone", "Rowling, J.K.",
|
||||
decimal.Decimal("7.99"))
|
||||
]
|
||||
|
||||
# connect to database
|
||||
connection = oracledb.connect(sample_env.get_main_connect_string())
|
||||
cursor = connection.cursor()
|
||||
|
||||
# create queue
|
||||
books_type = connection.gettype(BOOK_TYPE_NAME)
|
||||
queue = connection.queue(QUEUE_NAME, payload_type=books_type)
|
||||
queue.deqoptions.wait = oracledb.DEQ_NO_WAIT
|
||||
queue.deqoptions.navigation = oracledb.DEQ_FIRST_MSG
|
||||
|
||||
# dequeue all existing messages to ensure the queue is empty, just so that
|
||||
# the results are consistent
|
||||
while queue.deqone():
|
||||
pass
|
||||
|
||||
# enqueue a few messages
|
||||
print("Enqueuing messages...")
|
||||
for title, authors, price in BOOK_DATA:
|
||||
book = books_type.newobject()
|
||||
book.TITLE = title
|
||||
book.AUTHORS = authors
|
||||
book.PRICE = price
|
||||
print(title)
|
||||
queue.enqone(connection.msgproperties(payload=book))
|
||||
connection.commit()
|
||||
|
||||
# dequeue the messages
|
||||
print("\nDequeuing messages...")
|
||||
while True:
|
||||
props = queue.deqone()
|
||||
if not props:
|
||||
break
|
||||
print(props.payload.TITLE)
|
||||
connection.commit()
|
||||
print("\nDone.")
|
||||
@ -1,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()
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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())
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user